-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
I know that super
has been discussed many times before (#2638 and #3436 for example), but in my opinion this is something new. This issue is not about changing super
totally, but merely about defining its edge cases. The reason I started this discussion is because of issues I bumped into when making #3785.
This issue is not about what super
without parentheses should do etc. It is only about what super
should refer to in different cases.
The only way I’ve ever used super
, and the only way I’ve even seen super
be used, is like this:
class A extends B
method: -> super
That is, only in an instance method defined using the method:
syntax inside a class
body with extends
.
If that was the only case super
was allowed, compiling it would be trivial. The above could compile to:
var A;
A = (function(_super) {
__extends(A, _super);
function A() {
return _super.apply(this, arguments);
}
A.prototype.method = function() {
return _super.prototype.method.apply(this, arguments);
};
})(B);
CoffeeScript also allows super
in “static” methods:
class A extends B
@method: -> super
That would also be easy to compile:
var A;
A = (function(_super) {
__extends(A, _super);
function A() {
return _super.apply(this, arguments);
}
A.method = function() {
return _super.method.apply(this, arguments);
};
})(B);
If dynamic keys are implemented, that’d be straightforward too:
class A extends B
"#{foo()}": -> super
var A;
A = (function(_super) {
var _name;
__extends(A, _super);
function A() {
return _super.apply(this, arguments);
}
A.prototype[_name = "" + foo()] = function() {
return _super.prototype[_name].apply(this, arguments);
};
})(B);
So far so good. All nice and simple.
CoffeeScript also allows a “classic” style for making “classes”:
A = ->
A extends B
A::method = -> super
Note that the above isn’t equivalent to the class
examples, since the constructor A
doesn’t apply B
. That could be done by writing A = -> B.apply this, arguments
manually, but it would be much nicer to be able to write A = -> super
, but that throws an error.
There is no way to make a static method call super
using the “classic” style. A.method = -> super
throws an error.
So when is it possible to use super
“classic” style?
A = -> super # throws
A.m = -> super # throws
A::m = -> super # works
A.prototype.m = -> super # works
A["prototype"].m = -> super # throws
A["pro" + "totype"].m = -> super #throws
A[prototype].m = -> super #throws
According to #1392 you should be able to namespace classes (properly implemented in #3785):
namespaced.A::m = -> super # works
So what’s the problem? First of all, we don’t know the superclass, unlike in the class
examples above. Therefore, the superclass is assumed to be accessible at A.__super__
.
-
A
: Could be compiled to useA.__super__.constructor
. -
A.m
: Could be compiled to useA.__super__.constructor.m
. -
A::m
,A.prototype.m
: No problems. If we allow the above though, it might be confusing thatsuper
refers to different things inA.Prototype.m
andA.prototype.m
. -
A["prototype"].m
: IfA.prototype.m
is allowed, shouldn’t this also be? Ok, we could do it. -
A["pro" + "type"].m
: IfA["prototype"].m
is allowed, shouldn’t this also be? Ough, I see where this is going. -
A[prototype].m
: IfA["prototype"].m
is allowed, I’d expect this to be allowed, too. However, we don’t know ifprototype is "prototype"
.A[f()].m=->super
could compile to:var _super, _ref; _super = (_ref = f()) === "prototype" ? A.__super__ : A.__super__.constructor; A[_ref].m = function() { _super.apply(this, arguments); };
But perhaps that single-time runtime check is a bit weird. Also it feels odd that
A[f()].m=->super
does different things tosuper
depending on the value off()
. Therefore it might be better to compile it to assume that it always means a definition of a static method. I mean, why would you ever use something else thanA::m
orA.prototype.m
if you really wanted to create an instance method? There’s no reason to.But even if
A[b].m
always meant static method, shouldA["prototype"].m
too? What aboutA["pro" + "type"].m
?
There’s also the case where super
is used in an object literal:
extend A,
method: -> super
However, why use an extend
library function when you’re using CoffeeScript and have extends
? And shouldn’t that extend
function also provide some alternative to super
? So I guess this case should be disallowed, just like [1, (-> super), 2]
is.
Summing up, this is what I think:
The A.__super__
thing is ugly.
super
is not “classic”: It does not belong to the “classic” style of making classes. If you’re using the classic style you’re not using super
—instead, you might use some manual/“classic” way of doing it.
So in my opinion, super
should only be allowed in a function after a method:
or @method:
in a class
body. Nowhere else. That’s where they belong, and that’s where they’re simple. That’s also the only place I’ve ever seen them used. Compile it like the first two examples. Keep it simple! And as far as I understand, super
will only be available in class
blocks in ES6 (I might be wrong, though).
If that proposal is not acceptable, I’d say that super
should refer to:
A = -> super
→ A.__super__.constructor
A.method = -> super
→ A.__super__.constructor.method
A.prototype = -> super
→ A.__super__.constructor.prototype
A.prototype.method = -> super
→ A.__super__.method
A.Prototype.method = -> super
→ A.Prototype.__super__.constructor.method
A[anything].method = -> super
→ A[anything].__super__.constructor.method
A["prototype"].method = -> super
→ A["prototype"].__super__.constructor.method
A known “gotcha”. `[]` notation always means static method. CoffeeScript should not try to interpret the code inside the brackets.
Oh, and if anyone brings up ES6 super
, as far as I understand super()
calls the method with the same name of the superclass, while super.anySuperMethod()
calls “anySuperMethod” of the superclass.
What are people’s thoughts?