-
Couldn't load subscription status.
- 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: -> superThat 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: -> superThat 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()}": -> supervar 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 = -> superNote 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 #throwsAccording to #1392 you should be able to namespace classes (properly implemented in #3785):
namespaced.A::m = -> super # worksSo 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 thatsuperrefers to different things inA.Prototype.mandA.prototype.m. -
A["prototype"].m: IfA.prototype.mis allowed, shouldn’t this also be? Ok, we could do it. -
A["pro" + "type"].m: IfA["prototype"].mis allowed, shouldn’t this also be? Ough, I see where this is going. -
A[prototype].m: IfA["prototype"].mis allowed, I’d expect this to be allowed, too. However, we don’t know ifprototype is "prototype".A[f()].m=->supercould 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=->superdoes different things tosuperdepending 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::morA.prototype.mif you really wanted to create an instance method? There’s no reason to.But even if
A[b].malways meant static method, shouldA["prototype"].mtoo? What aboutA["pro" + "type"].m?
There’s also the case where super is used in an object literal:
extend A,
method: -> superHowever, 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?