It seems the curry and partial functions do the exact same thing. (Maybe the only difference is the number of arguments)
Is it just matter of convinience or there is a good reason on having two functions that do kind of similar thing.
It seems the curry and partial functions do the exact same thing. (Maybe the only difference is the number of arguments)
Is it just matter of convinience or there is a good reason on having two functions that do kind of similar thing.
A lot of answers from the wider FP community might steer you a little wrong. Ramda's currying seems to me to carry the spirit of currying from ML-style languages into Javascript, but is not strictly the same.
Partial application is probably fairly standard in Ramda. (Disclaimer: I'm one of the Ramda authors.) It's also much easier to describe. Ramda's partial
function accepts a function of n
arguments and a list of k
arguments (for some 0 < k < n
), and returns a new function of n - k
arguments that will call the original function with your new arguments and the original ones:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const g = partial(f, [1, 2]);
g.length; //=> 3
g(3, 4, 5); //=> 15
g(3); //=> NaN ≍ 1 + 2 + 3 + undefined + undefined)
// g :: (c, d, e) -> 1 + 2 + c + d + e
The function returned is just a simple function of the remaining parameters. If you call it with too few, it will act as though you called the original function with too few as well.
Currying is a slightly different story. In many languages, a curry
function would convert a function of n
parameters into a nested sequence of 1-parameter function, so that (a, b, c) => f(a, b, c)
transforms into a => (b => (c => f(a, b, c))
, which can be written without confusion as a => b => c => f(a, b, c)
. In Ramda, we are a little more flexible, allowing you to supply as many of the arguments as you choose at a single call, each time returning a function until you have supplied enough total parameters to satisfy the original function, at which point we call it and return that value. It's probably easier to explain with examples:
const f = (a, b, c, d, e) => a + b + c + d + e;
// f :: a -> b -> c -> d -> e -> a + b + c + d + e
const h5 = curry(f);
h5.length; //=> 5
const h3 = h5(1, 2);
h3.length; //=> 3
h3(3, 4, 5); //=> 15
const h2a = h3(3);
h2a.length; //=> 2
h2a(4, 5); //=> 15
const h2b = h5(1, 2, 3);
h2b.length; //=> 2
h2b(4, 5); //=> 15
const h2c = h5(1)(2, 3);
h2c.length; //=> 2
h2c(4, 5); //=> 15
const h2d = h5(1)(2)(3);
h2d.length; //=> 2
h2d(4, 5); //=> 15
const h1a = h3(3, 4);
h1a.length; //=> 1
h1a(5); //=> 15
const h1b = h2a(4);
h1b.length; //=> 1
h1b(5); //=> 15
// h5 :: (a, b, c, d, e) -> a + b + c + d + e
// :: (a, b, c, d) -> e -> a + b + c + d + e
// :: (a, b, c) -> (d, e) -> a + b + c + d + e
// :: (a, b, c) -> d -> e -> a + b + c + d + e
// :: (a, b) -> (c, d, e) -> a + b + c + d + e
// :: (a, b) -> (c, d) -> e -> a + b + c + d + e
// :: (a, b) -> c -> (d, e) -> a + b + c + d + e
// :: (a, b) -> c -> d -> e -> a + b + c + d + e
// :: a -> (b, c, d, e) -> a + b + c + d + e
// :: a -> (b, c, d) -> e -> a + b + c + d + e
// :: a -> (b, c) -> (d, e) -> a + b + c + d + e
// :: a -> (b, c) -> d -> e -> a + b + c + d + e
// :: a -> b -> (c, d, e) -> a + b + c + d + e
// :: a -> b -> (c, d) -> e -> a + b + c + d + e
// :: a -> b -> c -> (d, e) -> a + b + c + d + e
// :: a -> b -> c -> d -> e -> a + b + c + d + e
Because curry
is so much more flexible, I rarely use partial
myself. But there are people who are, ahem, partial to it.
As you briefly mentioned, curry
function can take a function with n arguments and return n functions with one argument. Currying is an essential tool to compose functions into higher order functions.
Partial applications of functions is a type of currying. In fact, if you look into Ramda source code you'll see that the partial
function was implemented using curry function.
var _arity = require('./_arity');
var _curry2 = require('./_curry2');
module.exports = function _createPartialApplicator(concat) {
return _curry2(function(fn, args) {
return _arity(Math.max(0, fn.length - args.length), function() {
return fn.apply(this, concat(args, arguments));
});
});
};
Also, take a look to this question that explain the different fundamentally.