May 16, 2018
curry
and compose
are two of the very helpful functions that I started to use them at work on a daily basis.
In most functional languages, functions are curried by default. So you can use partial application out of the box. Here’s a piece of Haskell:
add :: Int -> Int -> Int
add x y = x + y
increment = add 1
main = print (increment 41) -- 42
As described by its Hindley–Milner type signatures, add
takes an integer (x
) and returns a function Int -> Int
. By passing another integer (that is y
) to this function, it gives us the sum of the numbers.
Although in JavaScript functions are not curried by default, we can implement a helper to do that for us. curry
:
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn.call(null, ...args);
};
}
We can get the number of arguments that a function takes a.k.a. arity by getting the length
of that function. We keep checking to see if we have the number of arguments we need to execute the original function fn
, if we didn’t, we return a function with the given arguments already applied.
Here’s the JavaScript version of add
add = curry((a, b) => a + b);
increment = add(1);
increment(41) // 42
The idea behind compose
is, well, to compose functions. You build small reusable functions and compose them together to solve a more complex problem.
Here’s a piece of Haskell that composes two functions.
isOdd = not.even
main = print (isOdd 9) -- True
We pass the result of even
to not
.
JavaScript doesn’t have anything built-in to help us with that. Although there’s a proposal for a pipe operator that can help us to get a similar functionality, for now we can build something with what we already have. compose
:
function compose(...fns) {
const n = fns.length;
return function $compose(...args) {
let $args = args;
for (let i = n - 1; i >= 0; i -= 1) {
$args = [fns[i].call(null, ...$args)];
}
return $args[0];
};
}
We run a loop and accumulate the result of calling all the provided functions, from right to left. The returned value of each function is the argument of the next function (again from right to left).
Let’s write isOdd
in JavaScript
const even = x => x % 2 === 0;
const not = x => !x;
const isOdd = compose(not, even);
isOdd(9) // true
if the passed functions are curried, we can do even more beautiful stuff:
// helper methods from ramdajs
const hexToDecimal = flip(curry(parseInt))(16);
const getColor = compose(
map(hexToDecimal),
splitEvery(2),
replace("#", ""),
prop("color")
);
getColor({ color: "#1d97ff" }); // [29, 151, 255]
curry
and compose
help us to think in a more modular way. They improve the readability and reusability of code. If you want to go deeper, I recommend reading currying and Coding by Composing. If you have anything to say I’m fhdhsni on twitter.
Thanks for reading.
My personal blog,
Sharing my coding journey,