gkz / LiveScript

LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.
http://livescript.net
MIT License
2.32k stars 156 forks source link

Currying instance methods #597

Open johngeorgewright opened 10 years ago

johngeorgewright commented 10 years ago

I just attempted to use the curry syntax for an instance method and was surprised by the result:

class Dinner
  curry: (meat, flavour)-->
    @ # global scope

  make: ->
    add-flavour = @curry \chicken
    add-flavour \tikka

dinner = new Dinner
dinner.make!

To access the object's instance I have to bind the method:

class Dinner
  curry: (meat, flavour)~~>
    @ # dinner instance

Is this expected behaviour?

dead-claudia commented 10 years ago

Sounds like a bug.

dead-claudia commented 9 years ago

Basically, from what I'm gathering by the compiled output, the issue is that it should be bound always. The difference should be in whether it is bound to the instance or constructor, and it being always bound, IMO, wouldn't be a bad idea.

apaleslimghost commented 9 years ago

What's happening is the context is being discarded in curry$ because bound isn't set. So, rather than binding the method in the constructor, I think we can just do curry$(function() {}, true) when it's an instance method.

dead-claudia commented 9 years ago

I wasn't sure exactly how the binding should've been handled, but that sounds like a good idea.

apaleslimghost commented 9 years ago

Yep, and this way it can still be called in a separate context (curry$ passes down the original calling context), which wouldn't be possible if we did @method = @method.bind this.

I'll put in a pull request probably tomorrow.

apaleslimghost commented 9 years ago

Having battled with this all night (it was inexplicably transforming this into true), I think we'll have to rethink how to do this. Passing bound as true keeps the top-level context (the class instance), but in the case you want to bind the method to something else after partially applying it:

class A
  b: (c, d)--> this

a = new A
e = f: a.b \c
e.f \d #⇒ do we want a or e?

do we want to retain the A context or call it in the e context? The latter makes more sense given Javascript's semantics for this kind of thing.

vendethiel commented 9 years ago

if it's bound, you're not allowed to be-bind it.

dead-claudia commented 9 years ago

@vendethiel The question is about the semantics of curried class/instance methods. It appears that currying leads to nonobvious behavior, as detailed here. I don't know of any use cases for not binding an instance method. The case involving a partially applied curry looks ripe for bikeshedding here. It's an incompatibile fix, but it's a useful issue to address.

vendethiel commented 9 years ago

Oh. Alright, that's my bad.

Okay, we have two solutions:

dead-claudia commented 9 years ago

With your two options, 1 was already suggested by @quarterto, and 2 will require either a new helper or a partial rewrite of the existing one. If we go with the second, I believe a new helper would be better for compat reasons, since people already directly call the curry helper itself for various reasons (e.g. prelude-ls).

vendethiel commented 8 years ago

@gkz ?