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

YR Chen maian330 at
Mon Nov 12 13:40:14 PST 2007

Responding to 2 emails here...

On Nov 12, 2007 12:35 AM, Brendan Eich <brendan at> wrote:

> On Nov 11, 2007, at 4:26 PM, Brendan Eich wrote:
> > 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 :-/.
> Of course, Lars cut the Gordian knot in email just a bit later today:
>   type T
>   var v
>   x is T               // if T is type then the compiler knows that,
>                        // otherwise next case applies
>   x is v               // reflect::typeOf(x).isSubtypeOf(v)
>                        // fails if v not a Type object
>   x is { ... }         // interpret as record type
>   x is [ ... ]         // interpret as array type
>   x is ( ... )         // interpret as union type
>   x is function ...    // interpret as function type
>   x is null            // interpret as null type
>   x is undefined       // interpret as undefined type
>   x is like T          // T must be a type expression anyhow
> This allows arbitrary value expressions on the right of 'is', just so
> long as they don't look like type expressions in their leftmost
> token. Everyone wins. Yay?
> /be

Definitely like it. I wonder how convoluted the grammar change was - did it
require a new value_expr_no_conflicts_with_type_expr production and all
related productions/rules?

I don't want to rain on the parade, but I have some worries on how it would
impact an upgraded |instanceof|. More below.

On Nov 12, 2007 1:04 AM, Brendan Eich <brendan at> wrote:

> 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
> respectively).

I see - I was thinking that it was a technical limitation - but apparently
not. You've convinced me that pairing the semantics of |is| and type
notations is just as important, if not more, as pairing the syntax of |is|
and |instanceof|, so I'll drop the attempt to unify |is| and |instanceof|.

> > 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 :-).


> > 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

I'm aware that |like| is an unary type operator that can be used outside of
|is| exprs. It's just more convenient to write |is like| instead of "|is|
composed with |like|" :)

> > 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.

Yeah, I meant "anonymous interfaces". I know they're not the same as
interfaces, but they're similar in that they place a set of restrictions or
guarantees on a class or object that "implements" them.

> > 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.

I think that would be a good thing. Another thought: if |x is v|, where |v|
is a meta-object, works, then one would think that it should also work for
|like|, e.g.

var x = int
10 is x; // ok
10 is like x; // ?

But if that last statement is allowed, then so is |var y: like x|, which is
undecidable (if that's the correct term), so evidently |like| shouldn't work
with value exprs.

Might want to mention this in any clarification you put into the spec, even
if it's strictly disallowed in the formal grammar - that even though |is|
allows a value expr on the right side, it cannot be nested within a type
expr. That means given |var x|, none of |10 is like x|, |10 is (x)|, |10 is
{p: x}|, etc. are syntactically allowed.

> > 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.

Okay, this proposal stemmed from the error in your previous example, in
which you had |type x| where |x| was a value expr resolving to a
meta-object. I was thinking that if |type| could already recognize both
value exprs evaluating to meta-objects and type exprs, then it would be
trivial to implement this. Whether it is needed or not...see below.

> 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?
> /be

There are couple potential problems with upgrading |instanceof| to match the
syntax of the revised |is|:

1) Function expr syntax ambiguity. Consider:

a) x is function(p: int): int // ok
b) x is function(p: int): int {} // syntax error
c) x instanceof function(p: int): int // ok?
d) x instanceof function(p: int): int {} // syntax error?

Will it try to parse the |function ...| in (c)/(d) as a function expr or a
function type? (d), ignoring the type annotations, is currently legal in ES3
and is pretty much guaranteed to resolve to false. In the upgraded
|instanceof| however, if |function ...| is interpreted as a type, it would
be a syntax error. On the other hand, if |function ...| is interpreted as a
function expr, (c) would be a syntax error and it |instanceof| would no
longer match |is|. So treating |function ...| as a type will be backwards
compatible, but the compat issue is so minor (who the heck would do (d)?)
that I wouldn't mind it.

2) Object literal syntax ambiguity. If it is possible to simulate a
constructor with an object literal, namely creating an object with
[[HasInstance]], we'll have a similar situation to (1). I'm not sure if it's
possible, so I'll make up some syntax in the following example to indicate
what I mean:

x instanceof {meta::construct: function() {}}

That object literal is supposed to create a constructor-like object, but
like I said, I'm not sure if it's really possible and of the exact syntax
required. Anyway, that |{...}| is ambiguous in that it can either be treated
as a record structural type or an object literal. In ES3, there's no way to
create a constructor from an object literal (AFAIK), so the above syntax
(ignoring the namespace) would be a runtime error. Thus, there is no
backwards compat issue. If it were allowed in ES3, as before, it is
guaranteed to resolve to false. So the |{...}| should be treated as a record
structural type.

3) Parenthesis ambiguity. This is the most troublesome one. Consider:

a) x is (int) // ok
b) x is (some_constructor) // syntax error
c) x is ({p: int}) // ok
d) x instanceof (int) // ok
e) x instanceof (some_constructor) // syntax error?
f) x instanceof ({p: int}) // how should this be treated?

(e) is currently allowed in ES3, and unlike the the function expr ambiguity,
|(some_constructor)| is meaningful. That means it must be allowed in ES4 for
backwards compatibility. However, to match |is|, |(...)| must be treated as
a union type. And whether |(...)| is treated as a union type or a value
parens expr really matters as shown in (f). I'm stumped.

If we really want to upgrade |instanceof| yet keep it coherent with |is| in
both semantics and syntax, we may have to visit the |type| operator again,
but that should be a last resort.

-Yuh-Ruey Chen
-------------- next part --------------
An HTML attachment was scrubbed...

More information about the Es4-discuss mailing list