Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

super should be accessible within sub-functions of class methods #1606

Closed
devongovett opened this issue Aug 15, 2011 · 30 comments
Closed

super should be accessible within sub-functions of class methods #1606

devongovett opened this issue Aug 15, 2011 · 30 comments

Comments

@devongovett
Copy link

Right now the following CoffeeScript generates the error "cannot call super on an anonymous function":

class Test extends Foo
  method: ->
    setTimeout -> 
      super
      # ...
    , 1000

This makes sense for anonymous functions outside the scope of a class, but for those within class methods, like the one above, I think super should still be accessible. How difficult would this be to implement?

@michaelficarra
Copy link
Collaborator

... but what function do you expect it to call? It is an anonymous function after all.

@devongovett
Copy link
Author

The same one as if I didn't have the setTimeout function wrapping it. I edited the example and put in a class it extends (whoops). So in this example super should call Foo#method after 1 second. Basically it should recognize that the anonymous function is within a class method and super should work the same way it does everywhere else in that class method.

@michaelficarra
Copy link
Collaborator

Can you give a suggested compilation for the sample code in your original post?

@devongovett
Copy link
Author

Sure... Starting to realize it might be a little more complicated than I initially thought because you'll need access to this and arguments from the enclosing function, but we'll see... Here is an example for an unbound function which uses a _self variable and an _args variable.

Test = (function() {
  __extends(Test, Foo);
  function Test() {
    Test.__super__.constructor.apply(this, arguments);
  }
  Test.prototype.method = function() {
    var _self = this, _args = arguments;
    return setTimeout(function() {
      return Test.__super__.method.apply(_self, _args);
    }, 1000);
  };
  return Test;
})();

and here is one using a bound function using only an _args variable:

Test = (function() {
  __extends(Test, Foo);
  function Test() {
    Test.__super__.constructor.apply(this, arguments);
  }
  Test.prototype.method = function() {
    var _args = arguments;
    return setTimeout(__bind(function() {
      return Test.__super__.method.apply(this, _args);
    }, this), 1000);
  };
  return Test;
})();

I think this would be useful for a few cases, so let me know if you think it is worth the cost of those two extra variables or if you have a better way of doing this.

@philjackson
Copy link

It feels like - especially in a case where the fat arrow is used - super should be available to call the method. I would give a vote to this.

@lancejpollard
Copy link

This is the same way super works in Ruby, where super always applies to the context of the def method you're in. Check out the ActiveRecord::Callbacks code:

They're doing this:

module ActiveRecord
  module Callbacks
    # ...
    def create
      run_callbacks(:create) { super }
    end
  end
end

Also check out Yehuda Katz post on super in Ruby:

The current workaround for using super in this Ruby-esque way is something like this https://gist.github.com/1325392:

class Parent
  method: ->
    console.log arguments
    console.log "parent method called"

class Child extends Parent
  super: -> @constructor.__super__

  method: ->
    console.log "child method called"
    args = arguments
    self = @
    @another -> self.super().method(args...)

  another: (callback) ->
    console.log "another child method called"
    callback()

object = new Child
object.method("Hello World")

That's pretty much what Katz is saying you'd have to do if Ruby didn't support calling super from within blocks. This would be really useful to have in coffeescript.

At first glance it seems like calling super from within an anonymous or nested function should refer to that method, but when you start trying to read and use code like that you're always intuitively thinking super is referring to the visible method declaration on the class.

+1 for this feature!

@trevorsheridan
Copy link

+1 on this, especially when using =>

@jaekwon
Copy link

jaekwon commented Jan 28, 2012

+1, but I'd like super to be lexically scoped and independent of ->/=>.

@jaekwon
Copy link

jaekwon commented Jan 28, 2012

got something sorta working, methinks.
https://github.com/jaekwon/joe-script/commit/723237aef0858a4638422b97db65632d16952790
On my 'supersuper' branch.

@jaekwon
Copy link

jaekwon commented Jan 29, 2012

On second thought, I think it would be better to change super to simply be shorthand for this.constructor.__super__, and disallow the shorthand where super == super(arguments...).

This is more Pythonic, where you have to declare which method on super you are calling, e.g.

class Foo extends Bar
    constructor: ->
        super.constructor()
    method: ->
        super.method()

But it's more aligned with the nature of Javascript. Consider that decorated functions cannot know its own name at runtime:

class Foo extends Bar
    method: decorator ->
        super # the compiler could figure out that `method` is being called
              # but is it really worth it?

        super.method() # simple to implement, easy & consistent.

@jaekwon
Copy link

jaekwon commented Jan 29, 2012

I'm going to change super on my fork of Joe-Script soon. It will be backwards incompatible with Coffee-Script, unfortunately. And super will work as expected for => but probably not for -> as @trevorsheridan suggested.

@monshq
Copy link

monshq commented Feb 4, 2012

+1 on case with fat arrow

@ayush
Copy link

ayush commented Apr 19, 2012

another +1 on case with fat arrow

@chrisnicola
Copy link

+1 just ran into this today. Not being able to use super requires falling back to standard ugly javascript prototype calls whenever you need to wrap something in an asynchronous call.

@aseemk
Copy link
Contributor

aseemk commented May 13, 2012

This is a great idea. But I'm a bit confused -- why should super only work with fat arrows? The current super compiles to a statically bound ClassName.__super__.methodName.apply():

http://coffeescript.org/#try:class%20Foo%20extends%20Bar%0A%20%20baz%3A%20-%3E%0A%20%20%20%20super

So shouldn't super compile to this regardless of whether the anonymous function is a thin or fat arrow? (IOW, the current compilation doesn't rely on this, so why should it now?)

@philjackson
Copy link

I think it should work in both cases. Just feels especially relevant with fat arrows.

@devongovett
Copy link
Author

This appears to be closer to correct in 0.3.3 as released today. Thanks! At least it doesn't throw an error now... The example above compiles into this:

Test = (function(_super) {

  __extends(Test, _super);

  function Test() {
    return Test.__super__.constructor.apply(this, arguments);
  }

  Test.prototype.method = function() {
    return setTimeout(function() {
      return Test.__super__.method.apply(this, arguments);
    }, 1000);
  };

  return Test;

})(Foo);

Which is correct, except the this is wrong when calling super, unless you use a fat arrow. This could be confusing, so CoffeeScript should either throw a compile time error when calling super within a non-fat arrow function or make it use the correct this there. I think a compile time error would probably be better.

There is also a bug if the function is assigned to a variable where the wrong super is called. I can file a bug for this separately if you like. For example:

class Test extends Foo
  method: ->
    foo = -> 
      super

    foo()

compiles into:

Test = (function(_super) {

  __extends(Test, _super);

  function Test() {
    return Test.__super__.constructor.apply(this, arguments);
  }

  Test.prototype.method = function() {
    var foo;
    foo = function() {
      return foo.__super__.constructor.apply(this, arguments);
    };
    return foo();
  };

  return Test;

})(Foo);

which uses foo.__super__ which obviously doesn't exist since foo is not a class. It should use Test.__super__.

Again, I can file that one separately if you like.

@aseemk
Copy link
Contributor

aseemk commented May 15, 2012

Great thoroughness, @devongovett!

@pvande
Copy link

pvande commented May 24, 2012

+1 This bit me today, too.

In particular, it'll bite you if you try to store a reference to a function that invokes super.

class Foo extends Something
  bar: ->
    func baz: => super

Which generates:

Foo = (function(_super) {

  __extends(Foo, _super);

  function Foo() {
    return Foo.__super__.constructor.apply(this, arguments);
  }

  Foo.prototype.bar = function() {
    var _this = this;
    return f({
      baz: function() {
        return baz.__super__.constructor.apply(_this, arguments);
      }
    });
  };

  return Foo;

})(Something);

Clearly, there is no value baz, so this code will always fail. Binding that anonymous function to a variable has the problems that @devongovett mentioned.

As a work-around, I found that you can get an anonymous function that invokes the current method's super with [the following](http://coffeescript.org/#try:class%20Foo%20extends%20Something%0A%20%20bar%3A%20-%3E%0A%20%20%20%20args%20%3D%20arguments%0A%20%20%20%20callback%20%3D%20do%20(args...%29%20%3D%3E%0A%20%20%20%20%20%20%3D%3E%20super(args...%29):

class Foo extends Something
  bar: ->
    args = arguments
    callback = do (args...) =>
      => super(args...)

@laurent-descrivan
Copy link

Yes, calling super from sub-functions would be definitely great!

But (largely) based on @pvande 's tip, I now use a one-liner :

class Foo extends Something
  bar: ->
    call_super = do (args=arguments) => => super(args...)

    baz = ->
      call_super()
    baz()

Strange the first time, but it's not so bad and does trick.

@jaekwon
Copy link

jaekwon commented Jul 11, 2012

If you use the Cardamom clazz system, it gives you @super and you can use it like a property.

https://github.com/jaekwon/Cardamom/blob/master/src/clazz.coffee

Foo = clazz 'Foo', Bar, (_super) ->
  bar: ->
    baz = =>
      @super.method()

You do need to spell out the method though.

@summivox
Copy link

+1 for super.method(). Reason:

  • better semantics--needn't try to figure out "what method"
  • super would be pretty much just like this, avoiding special casing

@evanmoran
Copy link

+1 I completely agree on super.methodName() being independent of context. @smilekzs has a great point that this simplifies super in the same way @ simplifies this and I would add that :: simplifies prototype.

@shaunc
Copy link

shaunc commented Feb 24, 2015

+1 ... hmm... no progress or comments on this for a while. Is there a hitch?

@Burnett01
Copy link

+1 Would be good

@Blasz
Copy link

Blasz commented Mar 17, 2015

+1 Having to resort to __super__ and apply to use fairly standard OOP functionality seems to go against what coffeescript is about.

@lydell
Copy link
Collaborator

lydell commented Mar 17, 2015

@Blasz couldn't you have resorted to a => function?

@Blasz
Copy link

Blasz commented Mar 17, 2015

@lydell I was referring to super.method() instead of the subfunctions mentioned in the OP.

@mitar
Copy link
Contributor

mitar commented Oct 24, 2015

Hm, but arguments are then those of the inside function, not the method. :-(

@GeoffreyBooth
Copy link
Collaborator

This works in CS2:

class Test extends Foo
  method: ->
    setTimeout -> 
      super()
      #
    , 1000

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests