Responding to 2 emails here... On Nov 12, 2007 12:35 AM, Brendan Eich <[EMAIL PROTECTED]> 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 <[EMAIL PROTECTED]> 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 :-). > Doh! > > 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: http://bugs.ecmascript.org/ticket/ > 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
_______________________________________________ Es4-discuss mailing list Es4-discuss@mozilla.org https://mail.mozilla.org/listinfo/es4-discuss