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 http://bugs.ecmascript.org/ticket/103 -- 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 http://wiki.ecmascript.org/doku.php? id=proposals:iterators_and_generators 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 http://wiki.ecmascript.org/doku.php? >> 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 does.) > 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 http://wiki.ecmascript.org/doku.php? id=discussion:classes_as_structural_types_with_branding 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. /be _______________________________________________ Es4-discuss mailing list Es4-discuss@mozilla.org https://mail.mozilla.org/listinfo/es4-discuss