jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.49k stars 1.98k forks source link

Partial application syntax #2585

Closed gampleman closed 11 years ago

gampleman commented 11 years ago

There are some previous issues on currying/partial application #251, #1886, ...

They all seem kind of too complex to be of use in common everyday scripting and the form they take on seems to be hard to understand for someone unfamiliar with the concept, so I understand that they were not applied.

I would like this code:

add = (a,b) -> a + b

add_3 = add 3, ...

add_3(4) == 7

[1..4].map(add(2, ...)) == [3..6]

to compile to:

var add, add_3,
  __slice = [].slice;

add = function(a, b) {
  return a + b;
};

add_3 = function() {
  return add.apply(null, [3].concat(__slice.call(arguments)));
};

add_3(4) === 7;

[1, 2, 3, 4].map(function() {
  return add.apply(null, [2].concat(__slice.call(arguments)));
}) === [3, 4, 5, 6];

I would opt to not to support stuff like:

sum 3, ..., 5
sum 4, ..., 5, ...
vendethiel commented 11 years ago

I don't think this is gonna make it, CoffeeScript isn't that kind of functional language. I think underscore has sugar for this, or may want to look at @gkz /LiveScript (or wait for @michaelficarra /coffee-of-my-dreams)

rlidwka commented 11 years ago

@Nami-Doc, coffeescript is a functional language

michaelficarra commented 11 years ago

@Nami-Doc: AFAIK, LiveScript only has partially-applied operators. I actually really like this proposal. This is a great partial function application syntax.

vendethiel commented 11 years ago

LiveScript has partialization too. (and currying of course)

a = b 1, _

gkz commented 11 years ago

LiveScript supports both partial application and currying.

Partial application - using the _ placeholder.

add = (x, y) -> x + y
add-three = add _, 3

add-three 5 #=> 8

Currying (note the function is defined with a long arrow - -->)

add = (x, y) --> x + y
add-three = add 3
add-three 5 #=> 8
michaelficarra commented 11 years ago

Ah, I knew about the currying, but not the _ for partial application. @gkz: I can probably look it up, but others might be interested: is _ reserved only in that context, or everywhere?

gkz commented 11 years ago

_ is reserved in a couple of contexts - this, when defining backcalls, and when used after a case (aka |) to mean default.

You can still use _ normally as a variable, and it does not conflict with the use of underscore.js, for instance, as it is only reserved as a bare identifier.

_ = 5
f = g _.map

==>

var _, f;
_ = 5;
f = g(_.map);
gampleman commented 11 years ago

I thought about using the underscore, but the advantage of the proposed syntax (...) is that it is currently a compile error which means that adding it on shouldn't break any code.

It also has a natural name: the To Be Continued operator.

gkz commented 11 years ago

LiveScript doesn't conflict with underscore.js - that was my point... it would not break any code.

Also your proposal is pretty limited. If you can only omit the last argument, you might as well use currying and omit having to type anything at all. Eg.

add = (x, y) --> x + y
add-three = add 3
add-three 2 #=> 5

LiveScript's partial application syntax let's you omit any argument, and more than one:

add = (x, y, z) -> x + y + z
add-one = add _, 1, _
add-one 10, 9 #=> 20

I think it would be odd and confusing for the feature to be limited in the way you prescribe.

gampleman commented 11 years ago

add 1, _ currently compiles - therefore changing its semantics would affect current code, whereas add 1, ... doesn't. That was my point.

I am open to having it be less limited, but on the other hand the current proposal is more geared as a gentle introduction to the concept rather than a thing out of the functional programmer's dreams.

jashkenas commented 11 years ago

This syntax is pretty neat -- we should definitely discuss it further. My main concern about built-in partial application is use cases. Depending on how you tend to write your code, I find that partial application is extremely rare in JavaScript unless explicit in the function's API. You don't tend to do it to functions that don't do it for you...

gampleman commented 11 years ago

On further consideration I do find one problem with this proposal and that is on combining this with the OOP parts of the language. Consider:

class MyQuery
  constructor: (el) -> @els = document.querySelectorAll el

  bind: (event, fun) -> el.addEventListener(event, fun, false) for el in @els

  click: @bind('click', ...)
  focus: @bind('focus', ...)
  # etc...

The @bind here would currently refer to MyQuery.bind not to MyQuery.prototype.bind. The more correct syntax of click: @::bind('click', ...) does seem notably less appealing. While making this case an exception would solve this problem, it would muddle the semantics of the language.

Any ideas?

michaelficarra commented 11 years ago

Oh, very interesting. Is the context determined at the initial call site or the final call site? That's a very good question.

jashkenas commented 11 years ago

I'd love to see a pull request that tries to tackle this feature...

raganwald commented 11 years ago

I like partial application, I blogged about it recently: https://github.com/raganwald/homoiconic/blob/master/2013/01/practical-applications-of-partial-application.md

Using Ellipses for it seems nice.

jashkenas commented 11 years ago

Moving over to #2597.