allen at wirfs-brock.com
Tue Nov 1 09:45:36 PDT 2011
On Nov 1, 2011, at 6:53 AM, Jeremy Ashkenas wrote:
> This doesn't sound right to me. What happens if you call the same method on another object while the super-resolution is still active for the first call? IOW, this sounds like it has similar problems to dynamic scope; the behavior of a function becomes sensitive to the context in which it's called, which is unmodular.
> The problem isn't so much whether it's possible to come up with a semantics by changing the runtime; I'm sure we could do that. The problem is finding a way to get the semantics you want without taxing the performance all other function calls in the language. (Also known as a "pay-as-you-go" feature: if you don't use the feature, it shouldn't cost you anything.) We don't know how to do that for super().
Jeremy, have you looked at the current super proposal at http://wiki.ecmascript.org/doku.php?id=harmony:object_initialiser_super
It does not couple super use with classes at all. It does make it easy to define a super referencing method in an object or class literal, but the mechanism of super can be applied to any method defined in any object.
You are creating confusion by suggesting that the proposed ES6 super support "fails to work at all with regular prototypes".
> If you agree, then a super() that resolves dynamically is the way forward, and things get easier: super() can be added without classes, classes can be added without super(), and if both make it in, both can work seamlessly together.
I absolutely agree with your requirement and absolutely disagree with your conclusion regarding "dynamic" super.
The complication of super is that each "super call" requires two independent pieces of state in additional to the method arguments: the object that will be used as the |this| value of the resulting method invocation and the object where property lookup will begin when searching for the method. Let's call this the "lookup-point". |super| when used to access a method property really represents a pair of values (this,lookup-point). In the general case, these are not the same value so they must be independently captured and represented.
Where do these two pieces of state come from? The |this| value of a super call is simply the |this| that was dynamically passed into the calling method. Where does the look-up point come from. There are fundamentally two possibilities:
a) it is dynamically passed to every method invocation, ;just like |this| currently is
b) it is statically associated with the method in some way.
Each of these approach has some undesirable characteristics.
a) requires that an extra implicit argument must be passed to every method invocation. The value of this argument is the object from which the method was retrieved as an own property. This extra argument must be passed on every call because in general the call site does not know whether or not the callee references |super| so it must assume that it does. "Most" method calls take a single argument plus the implicit |this| argument (the actual argument statistics vary somewhat among languages but this is a good rule of thumb approximation for all OO languages including JS) so adding an additional lookup-point implicit argument is increasing the argument passing overhead of most calls by 50% yet super is only actually used by a single digit percentage of methods. Also note that after inline caching has been applied, that argument passing is a major part of the total overhead of a call. So, approach a) will have a negative performance impact on all calls for all programs.
b) static associates with each method a reference to it's lookup-poimt. This association is made when the method is "installed" in an object. This installation can occur syntactically, for example by defining a method as part of an object literal. It can occur dynamically, for example, via the Object.defineMethod function in the ES6 proposal. The advantage of this approach is that impose no overhead on method calls. It is complete pay-as-you-go. The disadvantage is that super-referencing methods be bound to a specific object. If you want to copy the method to a different object you have to copy the method and rebind it. Otherwise the method will remain statically bound to its original "home" object.
All modern OO languages that I am aware of have taken path b). This includes highly dynamic prototypal languages such as self. This is also the decision that was made when TC39 discussed this issue. b) is simply the best compromise to a set of conflicting goals.
Just saying "I don't think that an efficient, pay-as-you-go dynamic super() will be easy, but with the technical chops of TC39 at your disposal, it should be possible" is not helpful, particular when you make other statement that suggest that you really don't understand all the semantic subtitles of super. I hate to cite "authority" but the semantics and implementation alternatives of super has been thoroughly explored over 30+ years by object-oriented language designer and implementer including myself. This is not a new problem, the "technical chops" have already been supplied. A unique new solution that no one has ever though of before would be welcomed. If you have such a solution, please put it forward. But don't assume that it is something that has not already been deeperly consider both by TC39 member and other language designers.
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the es-discuss