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?
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.
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.
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.
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).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With