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

Brendan Eich brendan at
Sun Nov 11 16:26:49 PST 2007

On Nov 11, 2007, at 3:09 PM, Yuh-Ruey Chen wrote:

> Sorry, I'm still not getting it. The upgraded |instanceof| will behave
> exactly the same for the inputs non-upgraded |instanceof| works on.
> There are new guarantees, but they in no way affect the original ones.

They're stronger than the original ones, and different in kind too.  
Seems like apples and oranges mixed under one operator. Not obviously  
necessary given 'is', but here's a thought: instanceof does its  
prototype chain walk on the left operand, looking for an object equal  
to the value of the right operand's 'prototype' property, iff the  
right operand has [[HasInstance]] as function objects do in ES3.  
Otherwise it does a dynamic form of 'is', and for syntactic  
disambiguation, you may have to use unary 'type' on the right operand.

> There's no way to change the prototype chain of an object instantiated
> from a class, right? |obj instanceof klass| is practically  
> equivalent to
> |obj is klass|. Am I missing something?

No, classes are not really the issue. 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.

> Yes, that's one of the reason I'm confused. I didn't mention it
> explicitly, because the way Graydon replied seemed to imply to me that
> there was a very good reason that |is| requires type expressions,  
> but on
> second look, that's not the case.

See my followup mail, and --  
Graydon and the RI agreed with the last decision, from September -- I  
had forgotten about the exclusion of 'type E' to escape from value  
syntax on the right of 'is', in favor of making 'is' require a type  
expression only on its right.

> Actually the fixed example you gave (|let (t = type T) (a is type t)|)
> still confuses me. The first |type T| accepts a type expr and  
> evaluates
> to a meta-object, binding it to |t|. Now there are two possible
> interpretations for the |is| test:
> 1) |is| expects a type expr, so |type t| is evidently a type expr.

No, |type t| is a way to force a type expression t to be parsed as a  
value expression -- that's all.

> So
> this is the reverse of the previous usage of the |type| operator, i.e.
> this |type t| accepts a meta-object and evaluates to type expr. If  
> this
> interpretation is correct, this is extremely confusing.

Not correct. Sorry for adding to the confusion (temporarily) -- that  
RI session was meant to support 'is' taking a value expression on its  
right, but the RI doesn't do that per the resolution of #103. Ah, but  
you reply later to my subsequent messages. Ok, moving on:

> 2) |is| expects either a type expr or a meta-object, but in the latter
> case, that meta-object must be from a |type| expr. This is a weird
> restriction. Furthermore, I though the |x| in |type x| had to be a  
> type
> expr, yet in this case, |x| is a value expr (evaluating to the
> meta-object). Again, very confusing.

Agreed, and not what anyone wanted.

> So again, how exactly is |type| supposed to work?

It's supposed to parse its operand as a type expression and evaluate  
it to a value (meta-object).

> Namely, I can imagine
> a user coming from an ES3 background to instantly see the  
> usefulness of
> structural types, yet not care at all about classes, type  
> parameters, or
> early type checking.

Possibly, although structural types either are useful for 'is like'  
spot-checks (shape tests), or you end up defining names for them, and  
using those names to type objects, arrays, etc. created by  
initialisers, so as to get fixed property guarantees.

Classes come into the picture with 'wrap'. If you are upgrading an  
existing API with structural type annotations, but you can't afford  
to change all the callers to pass fixture-laden objects (this is  
highly likely; and it shouldn't be necessary to change all the  
callers of the API anyway), just structural type annotations will  
result in type errors for all calls that pass plain old dynamic  
objects and arrays. The API upgrader then has a choice: use 'like'  
for spot checks, or use 'wrap' for complete coverage (in case the API  
client mutates something behind the API implementations back).

GIven wrap, which creates some built-in nominal proxy type that does  
(efficient, we hope, not exponentially complex) checks on all reads  
and writes, you get type safety. But it can cost, even if optimized.  
As your API evolves and new users come along, if they can use ES4,  
you could provide nominal types for their use.

And even if you never expose nominal types in your API, the API's  
factory methods could return nominal types matching the structural  
type constraints in the API parameter and result annotations.

Just pointing out that even without early type checking, there are  
use-cases for programmable nominal types.

Type parameters are used by the iteration protocol (not yet  
implemented in the RI, but it's close). See 

This protocol underpins the for-in constructs.

>> Nit: your definition for (c) is not using "compatible with structural
>> type" the way we define the type compatibility relation (written "~:"
>> instead of "<:", see
>> id=spec:type_relations).
> BTW, I was mentioning the issue of the spec namespace being non-public
> because I couldn't access this page.

Sigh. I knew there was something not ancient and based on AS3  
mislocated there. Sorry again, we'll get that split out somehow.

> Hmm, I can see why it kind of makes sense for |is|, |cast|, and |wrap|
> all to require type exprs, considering they are all type operators.  
> But
> since |is| is a purely runtime check (pending Graydon's reply), it  
> makes
> no sense for it to not accept value exprs.

The tension between is/cast/wrap and is/instanceof is painful, isn't it?

> So far I see a couple solutions:
> 1) Let |is| work on value exprs, but leave |instanceof| alone. Then  
> the
> difference between the two resolves to whether the operand has a
> prototype chain. That is, the only inputs that both will work on are
> classes. It's still a gotcha, but a minor one.

Noted (more below).

> 2) Unify |is| and |instanceof| into a single |instanceof| operator.  
> I'm
> still not sure what you mean by guarantees, so at the moment I  
> don't see
> an issue other than |is| currently requiring a type expr.

Here's what I mean:

function F(){}
F.prototype = new Date
o = new F
assert(o instanceof F)
assert(!(o is F))

No mutation of the prototype property of F required (although that is  
significant in my view too). It makes no sense for (o is F) => true  
given that F is not a type. It's a function, and if you want function  
instances to stand for types, you need fixed type names for them or  
you lose decidability. But function definitions per ES3 can be  
replaced by new function definitions.

This by itself argues that instanceof and 'is' should not be combined.

> I also just
> learned from your post that AS3 already has an |is| operator that  
> works
> similarly, so that may be another barrier to this solution. | 
> instanceof|
> composed with |like| also sounds awkward, e.g. |instanceof like  
> type {a:
> int}|, unless it's the other way around, e.g. |instanceof type like  
> {a:
> int}|.

(More evidence of Adobe willingness to change ES4 from AS3, in case  
anyone needed it; but I'm still sympathetic to the argument that 'is'  
in ES4 should accept a value expression on its right, as 'is' in AS3  

> 3) Let |is| work on value exprs, and upgrade |instanceof| to work on
> everything |is| works on. Again, not sure what you mean be guarantees.

Let's split this into its two parts:

3a) Let 'is' work on value exprs -- hold this thought.
3b) Upgrade instanceof to do what it does in ES3, but instead of  
throwing a TypeError if its right operand has no [[HasInstance]]  
internal method, evaluate 'is' on its operands.

> This solution doesn't require the removal of |is| and so is more
> compatible with AS3. On the other hand, the fewer keywords, the  
> better.

I don't think we should combine instanceof and 'is', based on the  
example I gave above.

> 4) As you say (I think), |is| may be too overloaded. That it works  
> both
> classes/interfaces and structural types even though the two are very
> distinct tests is a cause for concern. Yet it is convenient to group
> them into one operator.

No, I don't agree (and I don't think I said anything like this). 'is'  
is <: the subtype relation, which is well-defined for all types.  
Types are not unrelated just because some use name for equivalence  
and identity, while others use structure. You can have a nominal type  
be a subtype of a structural type, and this is important -- of course  
you can't forge a subtype of a nominal type using a structural type,  
or even by nominal inheritance of the would-be superclass is final.

You may know that Modula 3 modeled nominal types by branding  
structural types. See 

As noted, this doesn't seem like the right way to model nominal types  
for several reasons. But it sometimes helps people who missed Modula  
3 to see the connections between nominal and structural types.

On the other hand, it's a bit of a mash-up, in my view, to make  
instanceof grow 'is' hair. The right operands are "very distinct"  
between those operators today. See what I mean?

> In addition to all that, if |is| works on value exprs, there's  
> still the
> confusion I've previously pointed out:
> // |b| is a class, |a| is instance of |b|
> a is type b
> a is b

That's less confusing than convenient, I think. But it is a bit of  
redundancy that should raise a flag. Part of the thinking in  
resolving #103 in favor of type expression on right of 'is' was to  
future-proof against a world where type and value expressions are  
combined somehow. I don't believe that world will come to pass,  
though. If it should, sooner is better. Restricting 'is' as #103-as- 
resolved did isn't making anyone too happy right now :-/.

> Maybe we can just explain to the user that |type| is only required for
> structural types to avoid syntactical ambiguity (in the same vein as
> block {} vs. object literal {})?

Yeah, that's why I wrote "less confusing than convenient". It's not  
that big of a deal compared to other issues here, IMHO.


More information about the Es4-discuss mailing list