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

Proposal: Deriving a function from an instance method that is bound/closed-over the instance – instance:method #4512

Closed
blitmap opened this issue Apr 18, 2017 · 15 comments

Comments

@blitmap
Copy link

blitmap commented Apr 18, 2017

Hello -

I was hoping to propose a syntax that would allow you to create a function with a bound/closed-over "this".

instance.method
instance:method
instance:method(...)

Would be:

class::method
class::method.bind(instance)
class::method.bind(instance, ...)

Please forgive me if I got something wrong here... I usually wind up submitting proposals when I'm too tired and there are often typos.

I believe Babel has something like this called the Bind operator:

https://babeljs.io/docs/plugins/transform-function-bind/

@vendethiel: You closed my other issue from yesterday but I corrected some typos :( Please go back and have another look?

@vendethiel
Copy link
Collaborator

FTR, this feature is available in Coco/LS.

@blitmap
Copy link
Author

blitmap commented Apr 18, 2017

@vendethiel, respectfully, I use Coffeescript because I think it strikes the right balance between being concise and expressive... :>

Looks like this is related to #3634

@@method # may be a bit clearer in intent

The above looks nicer, but you can't use it to bind over 'any instance' - only the current @

Same as in that issue, I would like this for attaching bound functions as event handlers in node.

@vendethiel
Copy link
Collaborator

@vendethiel, respectfully, I use Coffeescript because I think it strikes the right balance between being concise and expressive... :>

I just wanted to point out a case in the wild, and your issue link is great for that.


Looks like this is related to #3634

Well, not completly. The suggestion wants @@method as a shortcut to => @method .... Your suggestion here would cover more use cases, like console~bind, which can't be done with @@.

@blitmap
Copy link
Author

blitmap commented Apr 18, 2017

Using it within the definition of a class would look like this:

class Handler
   constructor: (node) ->
     $(node)
       .on('click', @:on_click)
       .on("mouseover", @:on_mouserover)

   on_click: -> #...
   on_mouseover: -> # ...

This is how it would be used confusingly:

instance:a.b.d
instance.a.b.d.bind(instance)

What's on the left of the : is the bind target.

instance.a.b:d
instance.a.b.d.bind(instance.a.b)

And then from the global context...

:console.log
window.console.log.bind(window)

The special cases are having @ or (nothing) on the left of :, which get replaced by this or the global/window.

I think this would create ambiguity when defining ranges.

An alternate could be:

instance@method

@GeoffreyBooth
Copy link
Collaborator

instance:method(...) syntax conflicts with current syntax for objects.

I'm not sure why we need a shorthand for this.

@boris-petrov
Copy link

@GeoffreyBooth - because we all have too much code which looks like this:

result = someList.map (item) =>
  @someMethod(item)

Instead of:

result = someList.map(this::someMethod)

We could use :: as in Babel's Bind operator. Even in Java such method references can be used (and I use them a lot), but in CoffeeScript, with the way this is bound, we cannot do that. I think this feature is a must.

@GeoffreyBooth
Copy link
Collaborator

Well, result = someList.map(this::someMethod) also already means something else. So you need a new syntax.

Please also show what your new proposed syntax(es) would compile into.

@boris-petrov
Copy link

Ah, I didn't even know this CoffeeScript syntax ::. :) I'm not the one to figure out the syntax, but we could "steal" from Coco .~ or use @@method as proposed in #3634.

As for the compiled output - it could either be:

obj.~method

compiles to:

obj.method.bind(obj)

Or use Babel's bind operator. Is there any issue with any of those? I guess the first one is preferable as it works everywhere without more transpilation.

@GeoffreyBooth
Copy link
Collaborator

Babel’s :: operator doesn’t even appear to be in Stage 1.

.bind(obj) doesn’t feel so long-winded to me, and personally I very rarely use bind so I don’t know why I would need a shortcut. This has been discussed before: #2136.

@boris-petrov
Copy link

Yes, exactly because this has been discussed before (actually many times) means that a lot of people need/want this. I also use bind very rarely but just because:

result = someList.map (item) =>
  @someMethod(item)

Is way prettier than:

result = someList.map(@someMethod.bind(this))

My 2 cents are - this is something I've personally wanted for a very long time and in my code I would use it a lot. I also do think that many people are like me (the at least 3 issues on the same topic over the years are a testimony for that I guess).

@connec
Copy link
Collaborator

connec commented May 5, 2017

I agree this could be a valuable addition, especially given the loss of bound methods. Whilst you're right that context.~method is not a big syntax saving over context.method.bind(context) or (args...) => context.method(args...) it does makes the intent a lot clearer.

items.map console.~log
# vs
items.map console.log.bind console
# vs
items.map (item) -> console.log item

I tend to end up using this most often when writing class wrappers around event-driven components, e.g.

el = React.createElement

class Widget extends React.Component # or a Stream or a Socket or ...
  render: ->
    onClick = @handleClick.bind @
    # When there are multiple events to be handled, this boilerplate adds up very quickly

    el 'div', { onClick }, 'Click me!'

  handleClick: ->
    doSomethingWith @

@lydell
Copy link
Collaborator

lydell commented May 5, 2017

it does makes the intent a lot clearer.

I think an anonymous function is clearer.

items.map console.log.bind console
# means:
items.map (item, index, array) -> console.log item, index, array
# not:
items.map (item) -> console.log item

@connec
Copy link
Collaborator

connec commented May 5, 2017

Ah, good point (and one I've been bitten by before, in fact!). console.log was a bad example (as was using it with map...), and you're right that anonymous functions give you control of the arguments at the call-site.

On the other hand, if you did want to forward all the arguments, there's even more duplication:

items.forEach console.~log
# vs
items.forEach -> console.log arguments... # relies on 'arguments', which may be undesirable
# vs
items.forEach (args...) -> console.log args... # duplication
# vs
items.forEach console.log.bind console # duplication
# vs
items.forEach (currentValue, index, array) -> console.log currentValue, index, array # duplication

Regardless, all the use-cases are pretty much centred around the one case of 'binding lots of callbacks', which I agree isn't such a common case as to definitely merit syntax. It is a confusing aspect of JS for people new to the language, and often a nuisance for experienced JS users, so having syntax for it could be quite widely appreciated.

@GeoffreyBooth
Copy link
Collaborator

For this to advance, it needs a specific syntax proposed, including what that syntax compiles to; explanations of how this new syntax handles the edge cases discussed in #2136; and why this should be accepted when #2136 wasn't (i.e. what makes this different than that, or why this shouldn't be closed for the same reasons).

@GeoffreyBooth GeoffreyBooth changed the title Deriving a function from an instance method that is bound/closed-over the instance -- instance:method Proposal: Deriving a function from an instance method that is bound/closed-over the instance – instance:method May 6, 2017
@GeoffreyBooth
Copy link
Collaborator

Closing as incomplete (see previous comment). If this gets fleshed out, and the community expresses interest, we can reopen.

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

No branches or pull requests

6 participants