super, self, and sideways & durable vs. template
On Jun 12, 2011, at 1:00 PM, Peter Michaux wrote:
Something I try to do when designing constructors is to ensure the constructed objects will be durable. That is, if some code modifies or breaks parts of the constructed object's exposed interface, the other parts of the exposed interface should continue working the same as before.
The flip side of "brittle" is "extensible": subclasses might break the base class, or they might usefully extend it. Some cases may need to delegate to the subclass (a la virtual in C++).
Part of what I use for this is what I term "sideways" calls because I don't know any better.
The closure pattern enables this kind of sideways call (not a bad term. I might just say closure call instead of method call, especially if you declare the methods using function definitions instead of assigning to vars.
My primary reason for asking here is I'm curious if there is any allowance for sideways calls in the proposed class syntax. By that I mean using the prototype parts of the proposed syntax rather than the closure based parts of the syntax (since the syntax can do both.)
Just to be clear to everyone following, you could use the closure pattern as Mark showed, for sure.
But to use the prototypal pattern, you would want something that has been discussed a bit: class elements (class body elements) defining once-per-class-evaluation function, let, and const bindings not as properties, but as lexical bindings in the scope of the prototype methods and accessors.
Currently such Declaration syntax is used to make prototype properties, although methods have a shorthand that obviates the need for "function m1() {...}" as a prototype-property-defining form:
class Monster {
...
attack(target) {
log('The monster attacks ' + target);
}
}
is (IINM, and I could be!) short for:
class Monster {
...
function attack(target) {
log('The monster attacks ' + target);
}
}
I've raised the issue among a couple of people and threads (although possibly not here on es-discuss) of whether the class syntax should rather avoid using declaration syntax to bind prototype properties. Declarations otherwise create lexical bindings.
It seems better to use a variant of the object initialiser extension syntax, although that can veer to far the other way and make a class body resemble a half-way merger of object initiialiser and module body. Say log were a Monster method. Then instead of:
class Monster { ... log(...rest) { /.../}
attack(target) { self.log('The monster attacks ' + target); } }
which as the brittle/extensible bug/feature you cite, you could write
class Monster { ... function log(...rest) { /.../}
attack(target) { log('The monster attacks ' + target); } }
where the function log definition makes a lexical binding in the class body's scope, not visible outside those braces, but statically scoping the attack prototype method.
If you wanted to expose log on the prototype, you'd need:
class Monster { ... function log(...rest) { /.../}
log = log;
attack(target) { log('The monster attacks ' + target); } }
per the current proposal, which is a queasy mix of assignment expression syntax with prototypal property creation via the extended object initialiser syntax (the attack method, in this case).
Putting a public in front of the assignment helps, slightly:
class Monster { ... function log(...rest) { /.../}
public log = log;
attack(target) { log('The monster attacks ' + target); } }
The class proposal does allow that much. I don't think going to PropertyAssignment (from the ObjectLiteral grammar) is right, though, since again (and even excluding the local declaration idea exploited by "function log..." in these sketches) the class body is not an object initialiser:
class Monster { ... function log(...rest) { /.../}
log: log; // yikes -- a bridge too far!
attack(target) { log('The monster attacks ' + target); } }
Each class element ends with ; or } as appropriate, and changing this to comma-separated PropertyAssignments does not pass basic smell tests.
Another avenue: even without a change to enable declarations in class bodies to bind once-per-class lexical bindings in the scope of the prototype methods, another alternative comes to mind: private prototype methods. With private name objects, these could be true properties of the prototype but not nameable outside of the class body's braced extent.
On Sun, Jun 12, 2011 at 2:17 PM, Brendan Eich <brendan at mozilla.com> wrote:
On Jun 12, 2011, at 1:00 PM, Peter Michaux wrote:
Something I try to do when designing constructors is to ensure the constructed objects will be durable. That is, if some code modifies or breaks parts of the constructed object's exposed interface, the other parts of the exposed interface should continue working the same as before.
The flip side of "brittle" is "extensible": subclasses might break the base class, or they might usefully extend it. Some cases may need to delegate to the subclass (a la virtual in C++).
Yes the extensibility of the template pattern is worth mentioning. The nice part of the closure system with its sideways calls is that it naturally allows control of when a method can be overridden in the template style and when it cannot.
My primary reason for asking here is I'm curious if there is any allowance for sideways calls in the proposed class syntax. By that I mean using the prototype parts of the proposed syntax rather than the closure based parts of the syntax (since the syntax can do both.)
Just to be clear to everyone following, you could use the closure pattern as Mark showed, for sure.
Yes. It seems that the major objection to the closure system is "efficiency". In the tradeoffs, some people weigh the extra memory and/or extra construction time heavily enough that the closure system does not appeal to them despite its other benefits. I'm trying to understand if the prototype side of the class proposal can do it all.
If you wanted to expose log on the prototype, you'd need:
class Monster { ... function log(...rest) { /.../}
log = log;
attack(target) { log('The monster attacks ' + target); } }
If the log function is going to use the data of the object then does it require binding "this"?
log.call(this, 'The monster attacks ' + target);
Peter
On Jun 12, 2011, at 4:29 PM, Peter Michaux wrote:
Just to be clear to everyone following, you could use the closure pattern as Mark showed, for sure.
Yes. It seems that the major objection to the closure system is "efficiency". In the tradeoffs, some people weigh the extra memory and/or extra construction time heavily enough that the closure system does not appeal to them despite its other benefits.
Prototypal has fans otherwise, since it overtly shares via C.prototype.
I'm trying to understand if the prototype side of the class proposal can do it all.
If there is only a class-body scope around prototype methods, then we avoid the per-instance unjoined function per method cost, yes.
If you wanted to expose log on the prototype, you'd need:
class Monster { ... function log(...rest) { /.../}
log = log;
attack(target) { log('The monster attacks ' + target); } }
If the log function is going to use the data of the object then does it require binding "this"?
It might not -- since it is class-private, the class can bind this when calling it explicitly via log.call or log.apply -- oops, you say that (must read ahead!):
log.call(this, 'The monster attacks ' + target);
But since log is class-private, we could pass |this| to it implicitly at no extra cost, when log is called from a method (prototype-homed function-valued property). Otherwise undefined would be passed as usual.
Would an implicit but not lexical (still dynamic, a 0th argument) |this| convention for private prototypal methods work? Formally I foresee some objecting. In practice it seems like it would work better this way than by wasting |this| on undefined and requiring explicit .call/apply.
On Sun, Jun 12, 2011 at 2:17 PM, Brendan Eich <brendan at mozilla.com> wrote:
But to use the prototypal pattern, you would want something that has been discussed a bit: class elements (class body elements) defining once-per-class-evaluation function, let, and const bindings not as properties, but as lexical bindings in the scope of the prototype methods and accessors.
I worry about treating a class body like a block scope and allowing variable declarations inside it. The syntax is really close to declaring properties inside the class (which is mostly what classes are for) and I worry people will get confused.
More so, if we allow that, it means a class body becomes "block-like". It'll be a lexical scope and you'll be able to declare variables in it. At that point, I fear people will start expecting any statement to work inside a class body. Once we open that can of worms, it gets really hard to find open syntactic space for the stuff we care about: being able to tersely declare class, prototype, and instance members.
As much as possible, I'd like to have a class body be just a collection of property definitions. I find that simpler to understand and less likely to cause us to paint ourselves into some syntactic corner.
I've raised the issue among a couple of people and threads (although
possibly not here on es-discuss) of whether the class syntax should rather avoid using declaration syntax to bind prototype properties. Declarations otherwise create lexical bindings.
Why not just declare them outside of the class?
class Monster { ... function log(...rest) { /.../}
attack(target) { log('The monster attacks ' + target); } }
If it was me, I'd probably prefer to just do:
module DungeonCrawl { export class Monster { attack(target) { log('The monster attacks ' + target); } }
function log(...rest) { /.../} }
To me, modules are a nice clean way to say "here's what I expose, and here's what I want to use just for myself."
class Monster { ... function log(...rest) { /.../}
public log = log;
attack(target) { log('The monster attacks ' + target); } }
Oh man, that's confusing looking.
Another avenue: even without a change to enable declarations in class bodies to bind once-per-class lexical bindings in the scope of the prototype methods, another alternative comes to mind: private prototype methods. With private name objects, these could be true properties of the prototype but not nameable outside of the class body's braced extent.
I really like this idea.
On Jun 13, 2011, at 10:38 AM, Bob Nystrom wrote:
On Sun, Jun 12, 2011 at 2:17 PM, Brendan Eich <brendan at mozilla.com> wrote: But to use the prototypal pattern, you would want something that has been discussed a bit: class elements (class body elements) defining once-per-class-evaluation function, let, and const bindings not as properties, but as lexical bindings in the scope of the prototype methods and accessors.
I worry about treating a class body like a block scope and allowing variable declarations inside it. The syntax is really close to declaring properties inside the class (which is mostly what classes are for) and I worry people will get confused.
Yes, this is my point too. I'm not actively endorsing declarations making temporaries in the class body, only pointing out how otherwise, declarations make lexical bindings -- not properties. Bit difference.
This suggests class body syntax should be sui generis. We should not cross syntax streams here.
More so, if we allow that, it means a class body becomes "block-like". It'll be a lexical scope and you'll be able to declare variables in it. At that point, I fear people will start expecting any statement to work inside a class body. Once we open that can of worms, it gets really hard to find open syntactic space for the stuff we care about: being able to tersely declare class, prototype, and instance members.
I don't think it's quite that hard, if you recall Mark's analogy to module bodies. Module bodies may contain declarations and statements. Only those declarations prefixed by export carry extra semantics peculiar to modules.
So in principle class bodies could be like module bodies, but then you would definitely need public (or proto, or some such novel contextual keyword) in front of the distinguished declarations.
Requiring such prefixing is too verbose. Consider the Point class I showed.
Also, module-exported declarations are still truly lexical bindings, until you reflect on a module and it reifies at runtime as an object with the exports showing up as fixed properties. This is fine as a reflection interface, but it does not seem right for classes to make prototype properties use lexical binding syntax, and indeed the current proposal mostly does not do that (more below).
As much as possible, I'd like to have a class body be just a collection of property definitions.
That is the other way to go: make the property definitions look more like object initialiser contents, but without comma separation.
However, the current proposal does this only for methods and accessors. For data properties (on the prototype, or on the class via static or a better prefix), what looks like an assignment expression, or a declarator-name and initialiser without a leading var-like keyword, is used.
I find that simpler to understand and less likely to cause us to paint ourselves into some syntactic corner.
Simpler is better, until it's simplistic -- Einstein's Knife.
I've raised the issue among a couple of people and threads (although possibly not here on es-discuss) of whether the class syntax should rather avoid using declaration syntax to bind prototype properties. Declarations otherwise create lexical bindings.
Why not just declare them outside of the class?
Fair point. We don't strictly need class-body-local declarations. They fall out of the module analogy, but I've argued against that above.
If class body syntax is special to its context, even if it reuses some parts of object initialiser syntax (which reuses function syntax in part for accessors and methods), then what should the syntax for prototype and class data properties be?
Prefixing with public, static, const, etc. wants declaration syntax. Using initialiser extensions without comma separation -- meaning with semicolon termination -- seems weird too, but it's more consistent.
Something I try to do when designing constructors is to ensure the constructed objects will be durable. That is, if some code modifies or breaks parts of the constructed object's exposed interface, the other parts of the exposed interface should continue working the same as before. Part of what I use for this is what I term "sideways" calls because I don't know any better. I don't know if there is a proper name for this type of call but it is different than super and self calls. Using sideways calls allows subclasses to still override interface methods but doesn't allow subclasses to change the behavior of non-overridden interface methods.
If anyone knows a name for this kind of call I'd be interested to know it and if there are class-based languages that have support for this type of call. It can be done by using very obscure method names, perhaps involving the class name as a namespacing technique, but this is not as elegant as what JavaScript, Scheme, etc allow as shown in the example below.
The following example shows that m3 in makeB is brittle because it uses a self call but that m4 is durable because it uses a sideways call. How would one make a durable m4 with the class syntax while still allowing m4 to be overridden in subclasses that choose to over ride it.
My primary reason for asking here is I'm curious if there is any allowance for sideways calls in the proposed class syntax. By that I mean using the prototype parts of the proposed syntax rather than the closure based parts of the syntax (since the syntax can do both.)
Thanks, Peter
function makeA() { return { m1: function() {return 'm1 in A';}, m2: function() {return 'm2 in A';}, m3: function() {return 'm3 in A';} }; }
function makeB() { var self = makeA(); var superM1 = self.m1;
}
// The makeC constructor breaks the m3 method even though it doesn't override the m3 method. // function makeC() { var self = makeB();
}