need some clarification on compile-time type vs. run-time type
Yuh-Ruey Chen
maian330 at gmail.com
Fri Nov 9 17:29:47 PST 2007
Graydon Hoare wrote:
> Yuh-Ruey Chen wrote:
> > I've been experimenting with the ES4 RI lately. I've discovered that the
> > following doesn't work:
> >
> > x = int;
> > 10 is x; // error
> > 10 to x; // error
> > 10 cast x; // error
> > var y: x; // error
> >
> > Of course, if I had used |type x = int|, it would work. Is this because
> > x is not a compile-time type?
>
> Roughly. Some refinements to terminology: there are no "compile-time
> types" or "run-time types". There are types (and interfaces, and
> classes). They get bound to property names. Some of those properties are
> fixed, some are not. Some fixed properties are additionally constrained
> to hold types, classes or interfaces. Some are not.
>
> Your example fails because x is not a fixed type-property. Properties
> introduced by 'type', 'class' or 'interface' are.
>
> Type names in type expressions only consult fixed type-properties,
> because we want evaluation of type expressions to be as predictable as
> possible. There are only ever 2 results to evaluating any type expression:
>
> #1 full evaluation to a specific "ground" type term
> #2 suspended partial evaluation due to incomplete information
>
> The flow of program control may affect which of these two results we
> get, and it can provide new information to restart a suspended
> evaluation -- by loading new code containing type definitions using
> 'eval', for example -- but it *cannot* invalidate a result of an
> evaluation. Types never need to be "re-evaluated" to reflect "mutation"
> in their environment. The evaluation of type expressions can (and
> should) memoize types.
>
Ah, so that's why I couldn't delete types. I ended up having to restart
the RI once the environment got cluttered with all these test types that
I defined. I need to get used to declaring them in scopes...
Speaking of which, there's a little bug in the RI: if you try to
constrain a property to specific type e.g. |var x: t| (is that the
proper terminology?) where t is undefined, then the interpreter
correctly reports an error, albeit one that didn't make sense until you
explained the "ground" type terminology. However, x remains constrained
to this undefined t, so attempts to redeclare x always result in an
error (even when trying to reconstrain it to the undefined t again),
e.g. |var x: int| results in an "incompatible redefinition of fixture
name" error. This bug probably doesn't matter in production, where such
errors are supposed to be fatal (I think), but in a REPL interpreter it
gets annoying.
> I find it a bit odd that |10 is x| didn't
> > work while |10 instanceof x| does work, considering that |is| is touted
> > as the successor to the |instanceof| operator. Or is |instanceof| the
> > run-time counterpart to |is|? Then should |instanceof| work on
> > everything that |is| can work on?
>
> instanceof consults the prototype chain. It happens to be the case that
> for classes, the prototype chains are arranged to follow the class
> inheritance hierarchy. But there are a variety of differences; for
> example, "x instanceof z" may return false in cases where "x is z"
> returns true (if z is an interface, say).
>
Then how would we do a check against an interface bound in a non-fixed
property? Is there an expression that does the same thing as |is| except
for non-fixed type properties?
The confusion I'm getting is that there seems to be many ways to check
or differentiate between types. For example, consider the following ES3
function:
function foo(x, t) {
if (!(x instanceof t))
throw some_type_error;
print(x);
}
If t were a fixed type property, then foo could be redefined as:
function foo.<t>(x) {
if (!(x is t))
throw some_type_error;
print(x);
}
Or maybe the following is possible (it's currently disallowed in the RI):
function foo.<t>(x: t) {
print(x);
}
Which one is preferred in ES4? The ES3 version is more flexible in a
way, since it treats types as first-class values, but the last version
is the most efficient. Users will have to deal with this choice, but it
requires a decent understanding of the type system to make a good choice.
And it doesn't end there. I haven't even addressed the |is like|
compound operator, of which there is no counterpart in |instanceof|
since structural types apparently can't be stored as non-fixed type
properties (|type x={a:int};y=x;| doesn't work in the RI).
> > These distinctions between compile-time types and these run-time types
> > (e.g. x in the above example) are subtle and confusing. I figure that
> > all these distinctions will trip many ES4 newcomers coming from an ES3
> > background.
>
> Perhaps. It's not clear what else we ought to do; the alternatives we've
> explored sound worse. Any suggestions?
>
I'm not sure how to phrase this, but it seems to me that ES4 is trying
to make fixed properties and non-fixed properties (and by extension,
type expressions and value expressions, and compile-time features and
run-time features) as similar and compatible as possible (e.g. |10 is
int| and |10 instanceof int|), yet there are evidently many cases where
they can't be interchanged (e.g. my first example). I know from
experience that the more similar concepts get, the higher the potential
for confusion, until they become the one and the same. And the two
concepts in question here cannot be one and the same if we want ES4 to
support efficient compilation. Perhaps, to reduce the confusion, the
differences between the two can be more pronounced, either through
syntax and/or behavior. I don't have any specific suggestions though.
At the very least, the differences and similarities need to be fully and
carefully documented. ES3 already has plenty of gotchas, and ES4 seems
to be introducing plenty more.
-Yuh-Ruey Chen
More information about the Es4-discuss
mailing list