<| with function RHS is brittle
On Mar 4, 2012, at 1:59 PM, Herby Vojčík wrote:
Hello,
I feel uneasy about this in <| proposal:
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.
It looks a little "perlish" to me, in the sense that when you write
x <| function (...) {...}
you must guard what x is because you can get two different outcomes (same as perl can use element in place of container, which backfires if the element itself is a container). I think it should be rephrased as:
If the RHS operand is a function expression then a. if the LHS operand has a property named prototype 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. b. otherwise TypeError exception is thrown.
Herby
Here1 is a specific instance of how this feature is useful: //define a non constructible superclass that provides some Smalltalk-like conventions const AbstractClass = Function.prototype <| { subclassResponsibility() {throw new Error(this.name+" did not implemented an abstract method")}, shouldNotImplement() {throw new Error(this.name+" should not implemented by "+this.name)}, name: "AbstractClass", prototype: Object.prototype <|{ get class() {return this.constructor}, error(message) {throw new Error(message)}, subclassResponsibility() {return this.class.subclassResponsibility()}, shouldNotImplement() {return this.class.shouldNotImplement()}, errorSubscriptBounds(index) {this.error("subscript is out of bounds: "+index)} } };
See the full js file for its use in context. But the basic idea is that we need to set up the root of both the class-side and instance-side inheritance chains. Your definition would make that impossible. This is an unusual use-case but it does occur when working at the root inheritance level. In addition to this specific example I think it will be useful when languages that are compile to JS need to implement their language-specific inheritance hierarchies while still exploiting the mechanism provided by JS.
I do want to intend to put some further though into the details of how I've specified this, but I think it is a capability that we need.
Allen
Hello,
I now realized that I had a mistake in my mental model and that the difference is smaller than I thought. Whatever LHS is, it always behaves the same: it is set as a {[Prototype]] of the RHS. Putting LHS.prototype into RHS.prototype is pure "addon". Seeing it this way, it is ok.
I was in the impression, that the behaviour differs in principle, and that different object are put in RHS.[[Prototype]]. I don't know why I thought that.
Maybe to clear that confusion, should it happen with others, it should be formulated in some way that dispels it.
Sorry for the false alarm,
Herby Vojčík wrote:
the same: it is set as a {[Prototype]] of the RHS. Putting LHS.prototype into RHS.prototype is pure "addon". Seeing it this way, it is ok.
LHS and RHS should be switched in the sentence.
I was in the impression, that the behaviour differs in principle, and that different object are put in RHS.[[Prototype]]. I don't know why I
RHS should be here.
thought that.
I know what I mean, I just wrote it wrong, sorry. Not writing 'LHS' and 'RHS' so often.
I was in the impression, that the behaviour differs in principle, and that different object are put in RHS.[[Prototype]]. I don't know why I RHS should be here.
LHS. These abbreviations are probably cursed in my case.
On Mar 5, 2012, at 2:45 AM, Herby Vojčík wrote:
I was in the impression, that the behaviour differs in principle, and that different object are put in RHS.[[Prototype]]. I don't know why I RHS should be here. LHS. These abbreviations are probably cursed in my case.
Welcome to the club!
Perhaps this will help conceptually:
All objects are created with a [[Prototype]] value. Each kind of literal has a default value that is used set the [[Prototype]] value. All <| does is allow an alternative value to be used to set [[Prototype]]. Function expression "literals" are special in that they create two objects instead of a single object. Each of those objects must be initialized with a [[Prototype]] value and there are default values supplied for both. <| when used with a Function expression allows setting both objects' [[Prototype]] values to non-default values. The technique used to specify those values is based upon the normal conventions for constructor functions and produce the expected results for "deriving" a new constructor function from an existing constructor function.
Allen Wirfs-Brock wrote:
Perhaps this will help conceptually:
All objects are created with a [[Prototype]] value. Each kind of literal has a default value that is used set the [[Prototype]] value. All <| does is allow an alternative value to be used to set [[Prototype]]. Function expression "literals" are special in that they create two objects instead of a single object. Each of those objects must be initialized with a [[Prototype]] value and there are default values supplied for both. <| when used with a Function expression allows setting both objects' [[Prototype]] values to non-default values. The technique used to specify those values is based upon the normal conventions for constructor functions and produce the expected results for "deriving" a new constructor function from an existing constructor function.
Yes, but it is still different operation based on LHS having or not having prototype property. I think <| should not have such special cases, see "extend for function" thread. It will make things clear and behaviour consistent even when "x <| function ..." is used. It is easier to explicitly say "I want set prototype" / "I want to set parallel hierarchy" (each by its own syntax) than trying to overcome this feature by
result = x <| function ...; if ('prototype' in x) { result.prototype = Object.create(Object.prototype, { constructor: { writable: true, enumerable: false, configurable: true, value: result } }); }
Yes, creation of "plain prototype" could be extracted to, say, Function.basicPrototypeFor(constructor) in which case it would be shorter, but the need for the if is ugly anyway. Special case to check always when I have a variable and I need to be sure.
Hello,
I feel uneasy about this in <| proposal:
It looks a little "perlish" to me, in the sense that when you write
x <| function (...) {...}
you must guard what x is because you can get two different outcomes (same as perl can use element in place of container, which backfires if the element itself is a container). I think it should be rephrased as: