Skip to content

What should super refer to? #3796

@lydell

Description

@lydell

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 use A.__super__.constructor.

  • A.m: Could be compiled to use A.__super__.constructor.m.

  • A::m, A.prototype.m: No problems. If we allow the above though, it might be confusing that super refers to different things in A.Prototype.m and A.prototype.m.

  • A["prototype"].m: If A.prototype.m is allowed, shouldn’t this also be? Ok, we could do it.

  • A["pro" + "type"].m: If A["prototype"].m is allowed, shouldn’t this also be? Ough, I see where this is going.

  • A[prototype].m: If A["prototype"].m is allowed, I’d expect this to be allowed, too. However, we don’t know if prototype 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 to super depending on the value of f(). 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 than A::m or A.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, should A["prototype"].m too? What about A["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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions