array subclassing and class declarations
This expresses an issue I have as well. the es:harmony proposal for classes just vaguely states it desugars into es5 things.
We may need more clarification on exactly what it desugars to and how you can interact with a "class" object outside the declarative class expressions.
On Oct 13, 2011 1:03 AM, "Allen Wirfs-Brock" <allen at wirfs-brock.com> wrote:
Alex, Erik, and I had a twitter conversation that eventually ran into the inevitable limit of effective communications using 140 character messages. This seems like a better place to capture and discuss the issues...
The conversation started about the new DOM API that Dart is proposing and my question of why a new language is need to prototype such an interface. Why not just develop it in JavaScript.
Erik mentioned that Proxies are needed and I replied that experimental implementations are available.
Alex also mentioned that a "use strict" for the DOM is needed, but we didn't follow up on that. I'd like to know more about what that meant.
Finally Alex said that he needs "extensible Array types" and said that "the committee hasn't moved quickly on that" . A subsequent message clarified that we was talking about "subclassing Array".
I said that <| supports "array subclassing" (in fact, this was the original use case that gave rise to <|) and Alex asked to be reminded how that would work and what about the length property.
I responded:
function createArraySubclass(proto, ...values) { return proto <| [...values]; }
If I'd had more characters I would have said that this creates a new object whose [[Prototype]] is proto and that the object has all the special characteristics of an array object. In particular Object.isArray will report true for it and the object has the special behavior of automatically updating the length property when elements are added past the end. It really is an array object, just one with a different [[Prototype]]. If you want it to also inherit from Array prototype you can define proto such as:
var proto = Array.prototype <| {/* additional "subclass" methods */};
Alex respond that new and instance of didn't work. (using new to create DOM nodes is one of the characteristics of the Dart DOM design).
It took me three tries to get all the typos out, but I showed how new could be made to work:
var SubArray = Array <| function(...values) { return Object.getPrototypeOf(this) <| [...values]; }
/* add subclass methods */ SubArray.prototype.method1 = function() {...}; SubArray.prototype.method2 = function() {...}; /*or stache it: SubArray.prototoype.{method1() {..}, method2() {}}; */
then you can say:
var s = new SubArray(1,2,3); console.log(s.length); //3 s[3] = 4; console.log(s.length); //4 console.log(Object.isArray(s)); //true console.log(s instanceof SubArray); // true console.log(s instanceof Array); //true
Alex responded that the proposed class syntax class NodeList extends Array { ... } would also do the trick.
I respond "does class extend as proposed, propagate over-ridden [[internal methods]]? Don't think so?"
Alex that I was being "class syntax hostile...Why the hate?" I plead 140-char terseness.
However, as I then pointed out, this is one of my issues with the class proposals. They have never been completed to the level of specifying how things like subclassing array should be handled. Alex apparently thought that class NodeList extends Array { } would automatically mean that NodeList instances would have full array instance semantics. I don't see how that would work. Would class ExtendedNodeList extends NodeList { ...} also result in a class whose instances had array instance semantics? How about in function makeClass(sup) { class newClass extends sup {...}; return newClass }; var c1 = makeClass(Array); var c2 = makeClass(c1); var c3 = makeClass(RegExp);
I just don't see how the class declaration would know when it did or didn't have to create special instance objects.
On a related matter, would you define a class whose instances are Proxy instances. The most recent discussions I followed seem to preclude using the trick I used above for arrays, something like:
class P extends Object{ constructor () { return Proxy.create(myHandler,Object.getPrototypeOf(this)); } }
because they disallow explicit returns from within constructors. Even if this was allowed what would happen for class Q extends P {...}
Is there anyway to subclass a proxy-based class and automatically get the proxy based behavior in the subclass instances?
We pretty much concluded with Alex asserting that the class declarations would provide a place to hang whatever (language design) hacks are needed to resolved such issues. And I asserted that I was more interested in making sure we had the primitive in the language that could be sugared (by library designers into these sorts of abstractions). Nothing new in that basic disagreement about approach.
Something we didn't get into, was the issue of how much of existing NodeList behavior you would really want to preserve in a new DOM design. I think that would be another interesting discussion.
Alex and Erik may have a different perspective on this little debate and may want to emphasize something that I glossed over. So, please do. Regardless, I think we stirred up some potentially interesting issues.
Le 13/10/2011 02:03, Allen Wirfs-Brock a écrit :
(...) If you want it to also inherit from Array prototype you can define proto such as:
var proto = Array.prototype<| {/* additional "subclass" methods */}; Alex respond that new and instance of didn't work. (using new to create DOM nodes is one of the characteristics of the Dart DOM design).
It took me three tries to get all the typos out, but I showed how new could be made to work:
var SubArray = Array<| function(...values) { return Object.getPrototypeOf(this)<| [...values]; }
I think that there is an ambiguity. (explained below)
/* add subclass methods */ SubArray.prototype.method1 = function() {...}; SubArray.prototype.method2 = function() {...}; /*or stache it: SubArray.prototoype.{method1() {..}, method2() {}}; */
then you can say:
var s = new SubArray(1,2,3); console.log(s.length); //3 s[3] = 4; console.log(s.length); //4 console.log(Array.isArray(s)); //true console.log(s instanceof SubArray); // true
So far so good
console.log(s instanceof Array); //true
i think that this should be false. Object.getPrototypeOf(s) is the value of Object.getPrototypeOf(this) in the constructor which is the anonymous function 'prototype' property (fresh object inheriting from Object.prototype with the methods you added). My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow: (anon function) --> Array --> Function.prototype --> Object.prototype --> null
And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
But I think that the following should work:
var SubArray = function(...values) { return Object.getPrototypeOf(this) <| [...values]; } SubArray.prototype = Array.prototype <| {/additional "subclass" methods/};
var s = new SubArray(1,2,3); // Array.isArray(s) === true, thanks to the "[...values]" syntax // Object.getPrototypeOf(s) === SubArray.prototype // SubArray.prototype is as follow: // {additional methods} --> Array.prototype --> Object.prototype --> null
However, as I said, the created object is an array thanks to the array syntax which doesn't apply to NodeLists or other DOM emulated constructs.
As I showed, applying <| to a function on the RHS may have (at least!) 2 semantics:
- creating a function and only setting its prototype (as in "let f = EnhancedFunctionPrototype <| function () {}" in the wiki)
- creating a constructor based on another constructor (which i think was your intention).
Maybe another operator should be introduced for the second case since there is currently no way in the language and the <| operator to distinguish how the LHS should be interpreted (as an object to set as [[Prototype]] or a constructor with some different semantics?) The class+extends keywords could be this "operator".
On Oct 13, 2011, at 5:15 AM, David Bruant wrote:
Le 13/10/2011 02:03, Allen Wirfs-Brock a écrit : ...
So far so good
console.log(s instanceof Array); //true i think that this should be false. Object.getPrototypeOf(s) is the value of Object.getPrototypeOf(this) in the constructor which is the anonymous function 'prototype' property (fresh object inheriting from Object.prototype with the methods you added). My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow: (anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named prototype and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototype property." (harmony:proto_operator )
But I think that the following should work:
var SubArray = function(...values) { return Object.getPrototypeOf(this) <| [...values]; } SubArray.prototype = Array.prototype <| {/additional "subclass" methods/};
you'd also need to include a constructor property in the new object in order to make s instanceof SubArray work properly. The <| spec was revised as above in order to ensure this all works automatically.
Le 13/10/2011 17:32, Allen Wirfs-Brock a écrit :
On Oct 13, 2011, at 5:15 AM, David Bruant wrote:
Le 13/10/2011 02:03, Allen Wirfs-Brock a écrit : ...
So far so good
console.log(s instanceof Array); //true i think that this should be false. Object.getPrototypeOf(s) is the value of Object.getPrototypeOf(this) in the constructor which is the anonymous function 'prototype' property (fresh object inheriting from Object.prototype with the methods you added). My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow: (anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named |prototype| and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the |prototype| property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s |prototype| property." (harmony:proto_operator )
I hadn't read that, sorry. I guess I just desugared this.
But I think that the following should work:
var SubArray = function(...values) { return Object.getPrototypeOf(this) <| [...values]; } SubArray.prototype = Array.prototype <| {/additional "subclass" methods/};
you'd also need to include a constructor property in the new object in order to make s instanceof SubArray work properly.
Why so? "A instanceof B" only looks at B.prototype (dynamic value, not the initial one) and A's prototype chain. I don't see why a constructor property matters.
On Thu, Oct 13, 2011 at 8:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
..
My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow:
(anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named prototype and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototypeproperty." (harmony:proto_operator )
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
We start off ok:
If the LHS operand has a property named prototype and the RHS operand is
a function expression,
So my LHS includes {prototype: aPrototype} and the RHS is "function() {...}". We start to hit bumps in the next bit:
then the [[Prototype]] of the function object is set to the LHS object
Hmm. I guess the operator will yield a function object and its prototype chain link will start with the LHS object.
and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototype property.
I guess "the new function" means the same as our preceding "the function object", both meaning the result of the operator. "a new object' means an empty object with a prototype-chain starting with the value of the aPrototype above.
If I get this right then:
var Result = {prototype: aPrototype, foo: baz} <| function aTempFunction() { this.a = 'b';}; var aResult = new Result();
gives me {a:'b', proto:{proto:aPrototype} }
Is it true? What happened to 'foo'? Couldn't I just say: var aResult = aPrototype <| {a: 'b'};
jjb
On Oct 13, 2011, at 12:23 PM, John J Barton wrote:
If I get this right then:
var Result = {prototype: aPrototype, foo: baz} <| function aTempFunction() { this.a = 'b';}; var aResult = new Result();
gives me {a:'b', proto:{proto:aPrototype} }
Is it true? What happened to 'foo'?
'foo' is a property of the aTempFunction.proto (to keep rolling with your naughty proto usage ;-).
It's a "class property" or "class static" or "class-side inherited property".
Couldn't I just say: var aResult = aPrototype <| {a: 'b'};
Sure, if you wanted a one-off and not a constructor with an inherited class property 'foo'.
On Oct 13, 2011, at 6:27 AM, Alex Russell wrote:
Hey Allen,
On Thu, Oct 13, 2011 at 2:03 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Alex also mentioned that a "use strict" for the DOM is needed, but we didn't follow up on that. I'd like to know more about what that meant.
What I meant is that politically it seems nearly intractable to get an idiomatic DOM done for JS. See the comments on my recent post on the topic of constructable function objects to get the flavor:
infrequently.org/2011/10/real-constructors-webidl-last-call, infrequently.org/2011/10/function-ality
and presumably, the "use strict" is an allusion to the idea that usage of such a idiomatic DOM would need to be "opt -in". I meant much the same thing in one of my tweets where I said that a new DOM design could manifest it self as an ES6 module. You would opt-in to the new DOM by importing that module.
Finally Alex said that he needs "extensible Array types" and said that "the committee hasn't moved quickly on that" . A subsequent message clarified that we was talking about "subclassing Array".
...
Alex responded that the proposed class syntax class NodeList extends Array { ... } would also do the trick.
I respond "does class extend as proposed, propagate over-ridden [[internal methods]]? Don't think so?"
Alex that I was being "class syntax hostile...Why the hate?" I plead 140-char terseness.
However, as I then pointed out, this is one of my issues with the class proposals. They have never been completed to the level of specifying how things like subclassing array should be handled. Alex apparently thought that class NodeList extends Array { } would automatically mean that NodeList instances would have full array instance semantics. I don't see how that would work.
It won't without explanation, but what you posted is just as magical, something I couldn't get out in 140 either = )
Arguably, pretty much everything a high level language does is pretty magical if you look at it from the perspective of machine language. But as language designers, our job is to specify how the language works such that the apparent magic is just sleight of hand. For us to have a language feature that we can actually implement and standardize somebody has to define exactly how it works. For <| we pretty much have that. For class declarations we don't.
Why do I say that? First, instead of peeling back what happens to .length and making the protocol for it explicit, the delegation system you're showing simply projects the magic of today's array.length into some new type. It's just as unsatisfying to my mind as hiding the details away using what I showed, except that it spills the guts of the hack out into the open, as though it were some sort of explanation.
The problem is, that from the perspective of the user level of the ES language, Array .length is magical. It does something that can not be expressed in the language. Proxies can be used to make explicit the the underlying sleight of hand that creates the magical illusion. However, Proxies are complex and heavy weight and as I further discussed they also do seem to particularly play well with class declarations. <| addresses the most common use case of this magical behavior by taking what is already a magical object (the [ ] )and explicitly setting its not-so-magical [[Prototype]]. As you pointed out, <| is really nothing more than a more constrained use of the non-standard proto property.
Perhaps, what you are really uncomfortable with is how .length actually works for arrays. To some degree, so am I. Rather than "subclassing" Array I probably would define NodeList as an object that encapsulates an Array instance. This is what you would have to do in almost any other language. Also, most OO design guidelines (both generic and language specific) will tell you not to using subclassing in that manner.
Would class ExtendedNodeList extends NodeList { ...} also result in a class whose instances had array instance semantics?
Yes. I'd hope so. But today we don't have anything in the language that would allow .length to work this way. What I'm getting at here is that we should fix THAT. Until then, either hack is just that, and the class syntax is more readable.
Except the class proposal lack the detailed specification that defines when and how the magic is applied, so it is neither implementable nor usable. The <| proposal is.
How about in function makeClass(sup) { class newClass extends sup {...}; return newClass }; var c1 = makeClass(Array); var c2 = makeClass(c1); var c3 = makeClass(RegExp);
I just don't see how the class declaration would know when it did or didn't have to create special instance objects.
Today, it would be magic. I dislike magic. I'm trying to prod us to do something like the Array.bless(this) explanation of the magic behavior that we'd talked about in the past. Some form that gives us a primitive that ends the archeology, not simply another way to project our inconsistency into new environments.
I actually don't see significant different between these approaches. One takes a regular object that exists in an arbitrary inheritance chain and forcibly applies magical array behavior to it. The other takes what is already a magical array and inserts it into an arbitrary inheritance chain. The net effect is pretty much the same. However, <| is (intentionally) more restrictive regarding how array magic an be applied.
What I think you may really be stating is a usability concern. You probably find that Array.bless(obj) expresses what is happening more clearly than someProto <| [ ] . If that is the case, I agree that to an uninformed reader Array.bless carries more implicit meaning than <| (or any other operator symbol that might be assign the same meaning). However, to really understand the mean of either construct and hence really understand what a program that uses them means you are going to have to know or at least look up the real meaning of the construct.
Here's a picture that may help: harmony:triangleupdate.pdf
On Oct 13, 2011, at 8:58 AM, David Bruant wrote:
Le 13/10/2011 17:32, Allen Wirfs-Brock a écrit :
you'd also need to include a constructor property in the new object in order to make s instanceof SubArray work properly. Why so? "A instanceof B" only looks at B.prototype (dynamic value, not the initial one) and A's prototype chain. I don't see why a constructor property matters.
You're right, its not necessary for instanceof. However, you probably should still add it for consistency with the prototype object that is automatically generated for a function.
On Oct 13, 2011, at 9:23 AM, John J Barton wrote:
On Thu, Oct 13, 2011 at 8:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: ..
My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow:
(anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named prototype and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototype property." (harmony:proto_operator )
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
Sorry, if the above definition is terse and jargony. However, the distinction between the [[Prototye]] internal property and the "prototype" property of a function (or any other object) is an important detail of the ES specification. I generally assume that readers of these specs. are up to speed on that.
We start off ok:
If the LHS operand has a property named prototype and the RHS operand is a function expression,
So my LHS includes {prototype: aPrototype} and the RHS is "function() {...}". We start to hit bumps in the next bit:
Yes, and that would be valid. However, it is important to also know that functions (almost) always have a "prototype" property than that for the mot common use case both the LHS and RHS would be a function.
then the [[Prototype]] of the function object is set to the LHS object
Hmm. I guess the operator will yield a function object and its prototype chain link will start with the LHS object.
yes. Also note from your first quote above that the "RHS operand is a function expression". Function expressions evaluate to function objects so "function object" is talking about the object produced by the RHS function expression. I could have been more explicit (but slight redundant by including a "RHS" in that second quote.
and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototype property.
I guess "the new function" means the same as our preceding "the function object", both meaning the result of the operator. "a new object' means an empty object with a prototype-chain starting with the value of the aPrototype above.
Yes sloppy language on my part. A function expression always evaluates to a "new function object".
If I get this right then:
var Result = {prototype: aPrototype, foo: baz} <| function aTempFunction() { this.a = 'b';}; var aResult = new Result();
gives me {a:'b', proto:{proto:aPrototype} }
The trickiness here is that function expressions really create two new objects. The actual function object and the object that is the value of it's "prototype" property. Each of those objects have their own (and different) inheritance chains through their distinct [[Prototype]] internal properties (what you are referring to via proto) . <| is setting both [[Prototype]] values appropriately so "foo" is an property of Result, but not of aResult.
On 10/13/2011 09:23 AM, John J Barton wrote:
On Thu, Oct 13, 2011 at 8:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com <mailto:allen at wirfs-brock.com>> wrote:
.. My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow:
(anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named |prototype| and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the |prototype| property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s |prototype| property." (http://wiki.ecmascript.org/doku.php?id=harmony:proto_operator )
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
I find the [[prototype]] prototype language confusing. Sometimes it helps me to understand the spec by (temporarily) replacing all occurrences of [[prototype]] with [[inherits from]]. Then that paragraph reads:
"If the LHS operand has a property named |prototype| and the RHS operand is a function expression then the [[inherits from]] of the function object is set to the LHS object and the |prototype| property of the new function is set to a new object whose [[inherits from]] is the value of the LHS’s |prototype| property."
In pseudo code...
if (LHS.hasOwnProperty("prototype")) { LHS.prototype <-- inherits from -- RHS.prototype (a new object created by <| ) LHS <-- inherits from -- RHS (a new function created by <| and the expression to the right of <| ) }
Or in more detail and browser specific pseudo code...
LHS = make_object_from_source_code_string(..left of <|..); if (is_source_code_string_a_function_expression(..right of <|..)) { if (LHS.hasOwnProperty("prototype")) { RHS = make_function_from_source_code_string(..right of <|..); RHS.prototype = {...}; RHS.prototype.proto = LHS.prototype; RHS.proto = LHS; } }
And so "var SubClassConstructor = ClassConstructor <| function () {}" will maintain the pseudo-clasical-oo-facade that exists in ES currently.
Jay
On Oct 13, 2011, at 11:07 AM, Jay Skeer wrote:
On 10/13/2011 09:23 AM, John J Barton wrote:
On Thu, Oct 13, 2011 at 8:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote: ..
My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow:
(anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named prototype and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the prototype property of the new function is set to a new object whose [[Prototype]] is the value of the LHS’s prototype property." (harmony:proto_operator )
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
I find the [[prototype]] prototype language confusing. Sometimes it helps me to understand the spec by (temporarily) replacing all occurrences of [[prototype]] with [[inherits from]]. Then that paragraph reads:
You aren't alone. I've actually encounter people who were implementing JS engines who were still confused by the distinction. For awhile, I tried to push that [[Prototype]] should be pronounced "parent" but that caused confusion with DOM usage.
This all arises from the language used in the ES specification. For the ES6 spec. I could, in theory, do a massive rename of [[Prototype]] to something else. However, I'm not sure whether such a renaming at this point in time wouldn't just add another layer of confusion. Of course, it's mostly only people who subscribe to this list that actually read the ES spec. The real problem is perhaps that the language, as viewed by its users, doesn't have a good non-ambigious way to talk about an object's [[Prototype]] value.
On Thu, Oct 13, 2011 at 11:16 AM, Allen Wirfs-Brock <allen at wirfs-brock.com>wrote:
This all arises from the language used in the ES specification. For the ES6 spec. I could, in theory, do a massive rename of [[Prototype]] to something else. However, I'm not sure whether such a renaming at this point in time wouldn't just add another layer of confusion. Of course, it's mostly only people who subscribe to this list that actually read the ES spec. The real problem is perhaps that the language, as viewed by its users, doesn't have a good non-ambigious way to talk about an object's [[Prototype]] value.
[[ProtoLink]] or [[ProtoRef]]?
We should use a word we can pronounce. When we pronounce it, no one should hear "prototype".
jjb
Le 13/10/2011 20:16, Allen Wirfs-Brock a écrit :
On Oct 13, 2011, at 11:07 AM, Jay Skeer wrote:
On 10/13/2011 09:23 AM, John J Barton wrote:
On Thu, Oct 13, 2011 at 8:32 AM, Allen Wirfs-Brock <allen at wirfs-brock.com <mailto:allen at wirfs-brock.com>> wrote:
.. My understanding of <| is that it sets the [[prototype]] property and nothing else. Consequently, I think that the semantics of "Array <| function(...){}" is to create a function with the prototype chain as follow:
(anon function) --> Array --> Function.prototype --> Object.prototype --> null And unless otherwise specified, (anon function).prototype is a fresh object inheriting from Object.prototype (not Array.prototype)
that was the original idea but a couple of months ago the <| spec was updated: "If the LHS operand has a property named |prototype| and the RHS operand is a function expression then the [[Prototype]] of the function object is set to the LHS object and the |prototype| property of the new function is set to a new object whose [[Prototype]] is the value of the LHS's |prototype| property." (http://wiki.ecmascript.org/doku.php?id=harmony:proto_operator )
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
I find the [[prototype]] prototype language confusing. Sometimes it helps me to understand the spec by (temporarily) replacing all occurrences of [[prototype]] with [[inherits from]]. Then that paragraph reads:
You aren't alone. I've actually encounter people who were implementing JS engines who were still confused by the distinction. For awhile, I tried to push that [[Prototype]] should be pronounced "parent" but that caused confusion with DOM usage.
This all arises from the language used in the ES specification. For the ES6 spec. I could, in theory, do a massive rename of [[Prototype]] to something else. However, I'm not sure whether such a renaming at this point in time wouldn't just add another layer of confusion.
There is a standard method named Object.getPrototypeOf. The problem would even go beyond confusion.
Of course, it's mostly only people who subscribe to this list that actually read the ES spec.
I started reading and referring to ES5 shortly before it became an official spec. I joined the mailing-list more than a year after that. I'm sure a lot of people read the spec but don't come here.
The real problem is perhaps that the language, as viewed by its users, doesn't have a good non-ambigious way to talk about an object's [[Prototype]] value. "The prototype of an object". Isn't it non-ambiguous enough?
Besides talking, I have made davidbruant.github.com/ObjectViz to represent visually objects. The fact of drawing different "flowers" show that the objects are different. Followed with voice over, I have no problem describing how object works in JavaScript. Based on my one-time JavaScript company training experience, the "flower" metaphor took off and it was easy to explain inheritance. I admit that this visualization is not easy to understand for newcomers to the language, but I could work on it. I'm open to suggestions.
Le 13/10/2011 21:03, Jay Skeer a écrit :
On 10/13/2011 11:37 AM, David Bruant wrote:
Le 13/10/2011 20:16, Allen Wirfs-Brock a écrit :
On Oct 13, 2011, at 11:07 AM, Jay Skeer wrote:
On 10/13/2011 09:23 AM, John J Barton wrote:
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
I find the [[prototype]] prototype language confusing. Sometimes it helps me to understand the spec by (temporarily) replacing all occurrences of [[prototype]] with [[inherits from]]. Then that paragraph reads: The real problem is perhaps that the language, as viewed by its users, doesn't have a good non-ambigious way to talk about an object's [[Prototype]] value. "The prototype of an object". Isn't it non-ambiguous enough?
foo.prototype is not foo's [[prototype]] (with a few rare exceptions, like Function)
I know. Both cases can be described unambiguously by saying:
- "The prototype of foo" or "foo's prototype"
- "the 'prototype' property of the foo object" Allen was talking about non-ambiguous ways to talk about prototype and i think such a thing exists.
On 10/13/2011 11:37 AM, David Bruant wrote:
Le 13/10/2011 20:16, Allen Wirfs-Brock a écrit :
On Oct 13, 2011, at 11:07 AM, Jay Skeer wrote:
On 10/13/2011 09:23 AM, John J Barton wrote:
For me this paragraph is a puzzle. A lot of the problem for me is "a property named prototype" vs [[Prototype]], two things with the same owner and same name to first order, but completely different in meaning. In addition we have function 'expression', function 'object', 'new' function and 'new' object.
I find the [[prototype]] prototype language confusing. Sometimes it helps me to understand the spec by (temporarily) replacing all occurrences of [[prototype]] with [[inherits from]]. Then that paragraph reads: The real problem is perhaps that the language, as viewed by its users, doesn't have a good non-ambigious way to talk about an object's [[Prototype]] value. "The prototype of an object". Isn't it non-ambiguous enough?
foo.prototype is not foo's [[prototype]] (with a few rare exceptions, like Function)
Jay
On Wed, Oct 12, 2011 at 17:03, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Finally Alex said that he needs "extensible Array types" and said that "the committee hasn't moved quickly on that" . A subsequent message clarified that we was talking about "subclassing Array".
I said that <| supports "array subclassing" (in fact, this was the original use case that gave rise to <|) and Alex asked to be reminded how that would work and what about the length property.
The latest ideas have desugared classes to <|.
The early ideas we had was to use "bless".
I'm also thrilled by your work on Date to make it extensible by moving things to use private names.
import name from "@name"; let time = name.create();
function Date(...) { this[time] = ... } Date.prototype = { ... };
This would allow "sub classing" using the standard JS pattern.
function MyDate(...args) { Date.call(this, ...args); } MyDate.prototype = Object.create{Date.prototype, { ... });
I was hoping we could explore this path a bit more. How can we do the same thing for Array? NodeList? HTMLButtonElement?
Unfortunately, every discussion we have had so far on classes [1] have derailed on things like temporal dead zones for const properties etc. This has prevented us to from reaching a point where we could talk about the more important semantic challenges.
I think both me and Alex are to blame for this. We want this so bad but we have yet to provide a consistent proposal on the wiki page.
[1] I was not present at the May and July meetings
On Oct 13, 2011, at 1:32 PM, Erik Arvidsson wrote:
On Wed, Oct 12, 2011 at 17:03, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:
Finally Alex said that he needs "extensible Array types" and said that "the committee hasn't moved quickly on that" . A subsequent message clarified that we was talking about "subclassing Array".
I said that <| supports "array subclassing" (in fact, this was the original use case that gave rise to <|) and Alex asked to be reminded how that would work and what about the length property.
The latest ideas have desugared classes to <|.
The early ideas we had was to use "bless".
I'm also thrilled by your work on Date to make it extensible by moving things to use private names.
...
I was hoping we could explore this path a bit more. How can we do the same thing for Array? NodeList? HTMLButtonElement?
We should be able to apply this technique any place "internal properties" are solely being used to represent private state. That means Date, the primitive wrappers, probably RegExp. (note these are all easy to fix in the spec. but implementors are also going to have to do some work to adapt to the new specification).
Array is different, because it isn't just about state It actually changes the semantics of property creation and other things. Function is different, well, because it is function. Simply using private names internally isn't going to make either subclass-able.
DOM objects fall into both buckets. Those that simply have private state could, in theory, work via the private name pattern (however, note that most DOMs are implemented in C++ so making them use private names to access internal state is probably not a trivial change). DOM object have modified object semantics face problems similar to Array. That is why it is important that we keep pushing WebIDL to minimize any semantics that doesn't directly map into something that is implementable in JS.
Proxy should be able to implement both Array and DOM object semantics (at least I'm tried to push to make sure that proxies can implement full Array semantics). However, the subclassing story (either class literals or <| based) isn't doesn't seem clear.
Le 14/10/2011 00:22, Allen Wirfs-Brock a écrit :
(...)
Proxy should be able to implement both Array and DOM object semantics (at least I'm tried to push to make sure that proxies can implement full Array semantics).
I'm confident we're getting there :-)
However, the subclassing story (either class literals or<| based) isn't doesn't seem clear.
Subclassing "proxy generators" (constructors which return a proxy) or DOM constructors?
What lacks in using <| + super?
Finding a new word would help. If one exists that can be used consistently. The natural-language expression “the protype of an object” would have to be changed in tandem with [[Prototype]] and the proto pseudo-property. Furthermore, the "prototype" property of a constructor and the prototype of an object are related a little bit.
Naming ideas: delegate, meta-object (which a prototype is, in some ways)
Alex, Erik, and I had a twitter conversation that eventually ran into the inevitable limit of effective communications using 140 character messages. This seems like a better place to capture and discuss the issues...
The conversation started about the new DOM API that Dart is proposing and my question of why a new language is need to prototype such an interface. Why not just develop it in JavaScript.
Erik mentioned that Proxies are needed and I replied that experimental implementations are available.
Alex also mentioned that a "use strict" for the DOM is needed, but we didn't follow up on that. I'd like to know more about what that meant.
Finally Alex said that he needs "extensible Array types" and said that "the committee hasn't moved quickly on that" . A subsequent message clarified that we was talking about "subclassing Array".
I said that <| supports "array subclassing" (in fact, this was the original use case that gave rise to <|) and Alex asked to be reminded how that would work and what about the length property.
I responded:
function createArraySubclass(proto, ...values) { return proto <| [...values]; }
If I'd had more characters I would have said that this creates a new object whose [[Prototype]] is proto and that the object has all the special characteristics of an array object. In particular Object.isArray will report true for it and the object has the special behavior of automatically updating the length property when elements are added past the end. It really is an array object, just one with a different [[Prototype]]. If you want it to also inherit from Array prototype you can define proto such as:
var proto = Array.prototype <| {/* additional "subclass" methods */};
Alex respond that new and instance of didn't work. (using new to create DOM nodes is one of the characteristics of the Dart DOM design).
It took me three tries to get all the typos out, but I showed how new could be made to work:
var SubArray = Array <| function(...values) { return Object.getPrototypeOf(this) <| [...values]; }
/* add subclass methods */ SubArray.prototype.method1 = function() {...}; SubArray.prototype.method2 = function() {...}; /*or stache it: SubArray.prototoype.{method1() {..}, method2() {}}; */
then you can say:
var s = new SubArray(1,2,3); console.log(s.length); //3 s[3] = 4; console.log(s.length); //4 console.log(Object.isArray(s)); //true console.log(s instanceof SubArray); // true console.log(s instanceof Array); //true
Alex responded that the proposed class syntax class NodeList extends Array { ... } would also do the trick.
I respond "does class extend as proposed, propagate over-ridden [[internal methods]]? Don't think so?"
Alex that I was being "class syntax hostile...Why the hate?" I plead 140-char terseness.
However, as I then pointed out, this is one of my issues with the class proposals. They have never been completed to the level of specifying how things like subclassing array should be handled. Alex apparently thought that class NodeList extends Array { } would automatically mean that NodeList instances would have full array instance semantics. I don't see how that would work. Would class ExtendedNodeList extends NodeList { ...}
also result in a class whose instances had array instance semantics? How about in function makeClass(sup) { class newClass extends sup {...}; return newClass }; var c1 = makeClass(Array); var c2 = makeClass(c1); var c3 = makeClass(RegExp);
I just don't see how the class declaration would know when it did or didn't have to create special instance objects.
On a related matter, would you define a class whose instances are Proxy instances. The most recent discussions I followed seem to preclude using the trick I used above for arrays, something like:
class P extends Object{ constructor () { return Proxy.create(myHandler,Object.getPrototypeOf(this)); } }
because they disallow explicit returns from within constructors. Even if this was allowed what would happen for class Q extends P {...}
Is there anyway to subclass a proxy-based class and automatically get the proxy based behavior in the subclass instances?
We pretty much concluded with Alex asserting that the class declarations would provide a place to hang whatever (language design) hacks are needed to resolved such issues. And I asserted that I was more interested in making sure we had the primitive in the language that could be sugared (by library designers into these sorts of abstractions). Nothing new in that basic disagreement about approach.
Something we didn't get into, was the issue of how much of existing NodeList behavior you would really want to preserve in a new DOM design. I think that would be another interesting discussion.
Alex and Erik may have a different perspective on this little debate and may want to emphasize something that I glossed over. So, please do. Regardless, I think we stirred up some potentially interesting issues.