need some clarification on compile-time type vs. run-time type

Brendan Eich brendan at
Sun Nov 11 23:04:02 PST 2007

On Nov 11, 2007, at 10:41 PM, Yuh-Ruey Chen wrote:

>> The key difference is that
>> instanceof results can change over time for a given object on the
>> left and plain old function on the right, while 'is' results cannot
>> vary over the lifetimes of the same operands.
> If |is| is a purely runtime check, does this really matter? Can you  
> give
> me a use case involving a runtime type check that requires that the  
> type
> check always be the same for a pair of operands?

Your poor-man's type parameters example will do. The 'is' operator is  
how you write an expression testing subtype, and the : type  
annotation colon enforces subtype relation for all writes to the  
annotated slot. So 'is' and annotations are closely related (ignore  
the hardcoded conversions within AnyString, AnyNumber, and AnyBoolean  

> Ok, thanks for the clarification. However, that still doesn't answer
> this question: if the |x| in |type x| has to be a type expr, then how
> did |type x| in the example work? |x| in that case was a value expr
> (evaluating to a meta-object), not a type expr.

Maybe that was just a mistake :-).

>> The tension between is/cast/wrap and is/instanceof is painful,  
>> isn't it?
> Indeed. And it's all so subjective too...

See latest mail from me, Lars's Gordian knot slicing. It makes 'is'  
DWIM. 'cast' need not accept value expressions since it is useful  
only for the type checker (optional strict mode), and for  
documentation purposes. 'wrap' should do what 'like' does (require a  
type expression, currently).

> As I said before, is there any value in making sure that the value  
> of |x
> is y| never changes for a given pair x and y? I can understand the
> importance of that guarantee for type annotations, but since |is| is a
> late type check, I don't see the value.

Even if (as we believe so far) 'is' should not be analyzed by strict  
mode, you want the parallel between type annotations and 'is' to hold  
up over the lifetime of the slot in the case of a type annotation,  
and the operands in the case of 'is'. Well, I do, at any rate. Both  
'let x:T' and 'x is T' invoke the subtype relation. And the prototype  
relation is not the subtype relation.

> FYI, I'm not really in favor of merging to the two operators. I do
> recognize that there is a fundamental difference between the
> |instanceof| test and the |is| test. But I could also say that  
> there is
> a fundamental difference between the |is| test and the |is like| test.

Remember, nothing special goes on with 'is like'. Imagine explicit  
parenthese (not allowed because they mean union type, but pretend):

   x is T
   x is (like T)

We know this must be the case since you could have defined

   type T = like U

before the first line (x is T), and the result should be the same as  
if you expanded the typename:

   x is like U

> On the other hand, unless structural types are mutable, |is| and |is
> like| are similar in that the values of both |is| and |is like| are  
> for
> a given pair of operands can never change. And structural types are
> immutable, right?

Right. But don't believe 'is like' is a compound-keyword type  
operator -- it's two operators, the right one a unary prefix type op  
(type constructor really).

> Yeah, misunderstood you. I can see now that record types are like
> undeclared interfaces.

Not sure if you meant anonymous interfaces, but interfaces can have  
only methods, which can be overridden along the inheritance chain in  
the nominal type system. Structural types on the other hand describe  
records with fields -- not methods, although a field's type could be  
a function structural type.

Bottom line: interfaces != structural object types.

> However, as I noted before, the |like| test for
> structural types is still a different beast.

There's a possible generalization of 'like' to accept any type on its  
right (conversion is not an issue: 
258). Remember, no non-compositional special cases or ad-hoc patches,  
if we can avoid 'em.

> A thought: If the |type| operator can accept a value expr that  
> resolves
> to a meta-object and returns that meta-object, then |type| can be
> chained, e.g. |type type type T| is equivalent to |type T|. In this  
> way,
> a user that's unsure if a particular type expression is ambigious can
> just prefix it with |type| without worries.

But the parser (not optional strict mode, the fundamental grammar)  
will reject a non-type expression (if we want it to). This is a case  
of load/edit/reload at worst.

But hey, I'm jazzed by Lars's "have our cake and eat it" proposal for  
'is'. For instanceof, we could do what you and I seem to be talking  
about: extend it to do what 'is' does, with the gravy that its right  
operand is always a value expression. So 'instanceof' could be used  
to handle all kinds of instantiation, ES3 mutable-prototype function  
constructors and ES4 types. Comments?


More information about the Es4-discuss mailing list