Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

n-ary curry in CoffeeScript

I was playing with CoffeeScript when I found myself writing the following lines and then looking at them in awe:

compose = (f, g) -> (x) -> f g x
curry = (f) -> (x) -> (y) -> f(x, y)
uncurry = (f) -> (x, y) -> (f x) y

How nice, did I think! Now, as an exercise, I thought I would generalize the curry and uncurry functions to n args, to obtain something similar to this:

curry2 = (f) -> (x) -> (y) -> f(x, y)
curry3 = (f) -> (x) -> (y) -> (z) -> f(x, y, z)
curry4 = (f) -> (x) -> (y) -> (z) -> (t) -> f(x, y, z, t)

And the same thing for uncurry:

uncurry2 =  (f) -> (x, y) -> (f x) y
uncurry3 = (f) -> (x, y, z) -> ((f x) y) z
uncurry4 = (f) -> (x, y, z, t) -> (((f x) y) z) t

Writing the n-ary uncurry was not very hard:

uncurry = (n) -> (f) -> (args...) ->
    if n == 1
        f args[0]
    else
        ((uncurry n - 1) f args.shift()) args...

On the other hand, I can't figure out how to get the n-ary curry to work. I thought of implementing first a curry_list function that is the generalization of this suite:

curry_list2 = (f) -> (x) -> [x, y]
curry_list3 = (f) -> (x) -> (z) -> [x, y, z]
curry_list4 = (f) -> (x) -> (z) -> (t) -> [x, y, z, t]

Here's the implementation:

curry_list = (n) ->
    curry_list_accum = (n, accum) ->
        if n
            (x) ->
                accum.push x
                curry_list_accum n - 1, accum
        else
            accum
    curry_list_accum n, []

And then I would just compose curry_list with function application to obtain currying. That's what I tried to do:

curry = (n) ->
    apply_helper = (f) -> (args) -> f args...
    (f) -> compose (apply_helper f), (curry_list n)

But for some reason, it does not work. For exemple, trying to evaluate

curry(3)((a,b,c) -> a + b + c)(1)(2)(3)

yields the following error:

Function.prototype.apply: Arguments list has wrong type

Now after jotting some more notes I understand that trying to compose f with curry_list is incorrect. I do have the intuition that what I'm looking for is something that looks like this composition, but is not exactly that. Am I correct in thinking that?

Finally, what would be a correct implementation?

like image 777
Noé Rubinstein Avatar asked Dec 04 '11 04:12

Noé Rubinstein


People also ask

What is difference between currying and partial application?

Currying: A function returning another function that might return another function, but every returned function must take only one parameter at a time. Partial application: A function returning another function that might return another function, but each returned function can take several parameters.

What is infinite currying in Javascript?

Join. Function Currying is a process in functional programming in which we transform function with multiple arguments into a series of nested functions that takes one argument and eventually resolve to a value.

What is a curry function?

A curried function is a function of several arguments rewritten such that it accepts the first argument and returns a function that accepts the second argument and so on. This allows functions of several arguments to have some of their initial arguments partially applied.

Is currying partial function application?

The transformed function is different from the original — it needs less arguments. Currying is not partial application. It can be implemented using partial application. You can't curry a function that takes any number of arguments, (unless you fix the number of arguments).


1 Answers

You are returning the composed function after curry(3)((a,b,c) -> a + b + c), not the accumulator.

That means ((args) -> f args...) is receiving a function as argument, your code doesn't wait until the argument list is complete to call f.

Maybe implement this without composition?

accumulator = (n, accum, f) ->
    return f accum... if n is 0
    (x) ->
        accum.push x
        accumulator n - 1, accum, f

curry = (n) ->
    (f) -> accumulator n, [], f

curry(3)((a,b,c) -> a + b + c)(1)(2)(3) # 6
like image 69
Ricardo Tomasi Avatar answered Oct 17 '22 07:10

Ricardo Tomasi