(⌒▽⌒)☆: 4 min. read
This was the content of one of my internal sharings I did within my team. Talking about one of the basic blocks of functional programming.
currying is a programming technique that reduces a multiple arguments function into a n sequence of functions that take exactly 1 argument.
uncurrying is the mathematical dual of the above statement. (transforms a n sequence of functions that take 1 argument into a function that takes n arguments)
its namesake is from Haskell Brooks Curry (not the food), who was an American mathematician and logician, (incidentally, his name spawned off 3 programming languages, go google if you are interested)
in more concrete terms, currying transforms a function eg: f(a,b,c)
into a callable f(a)(b)(c)
so for example, sum(1,2,3)
is how we would call an uncurried function
whereas in the curried version we call it like this sum(1)(2)(3)
what happens when we call a curried function halfway ie sum(1)(2)
?
what we achieve is a function that has its first 2 arguments bound. formally it is known as a partially applied function
incidentally we can replicate the behavior by using Function.prototype.bind
const sum = (x, y, z) => x + y + z;
const curriedSum = (x) => (y) => (z) => x + y + z;
function explicitCurriedSum(x) {
return function (y) {
return function (z) {
return x + y + z;
};
};
}
// we call like this sum(1,2,3)
// we call curried functions like this curriedSum(1)(2)(3)
const partiallyApplied = curriedSum(1)(2);
const equivalentPartiallyApplied = sum.bind(null, 1, 2);
so yea, if the output doesnt really change, just the way that we call the function differs, then why do we even bother to curry functions?
for functional programming enthusiasts, currying really helps them in joining their software together, and i think I dont need to elaborate further.
but for me, currying is that tool in your toolbox that you often overlook, but only when at your wits end, then you suddenly remember about it and use it.
one great thing about currying is that is reduces the complexity of a big function into smaller managable chunks. These manageable chunks can then be unit tested well enough that the developer is confident in their code.
writing this out, it really is difficult to find convincing use cases for currying, but to me they feel like unix pipes and redirection in their scale of utility, and once you get it, it will start appearing everywhere
also, our team uses currying abit (from what ive read so far) so knowing it is quite useful.
we already got a taste of how we can curry a function.
const curriedSum = (x) => (y) => (z) => x + y + z;
if we read this function properly, you will realise that we are returning functions at every argument provided.
for this particular curried function, we only accept up to a max of 3 arguments before returning the sum, it is a quite specific implementation.
can we do better?
in order to make things general, we would expect a curry function that takes a function and immediately gives the behavior of curry to it, no matter the number of arguments required.
you could implement it manually if you search up code for it (since its a well-established concept) but I wont because custom code usually is buggy.
so we will look at ramda.js's curry function.
so with ramda's curry function, we can automagically transform any function into its curried version.
incidentally, if you look at other programming languages, currying as default is provided out of the box if you write any function; One does not need to manually curry in those languages.
NB: curry doesnt play well with Typescript, only just recently they introduced Variadic Tuple Types in Typescript 4.0. with this, curry could be implemented with type-safety.
Previously, curry was almost impossible to implement or if you look at the type-signature of curry you wanted to vomit. dont even talk about the compiler errors.
hopefully I've introduced the concept to you guys as simply as possible, so that if in the wild, you see code like this, you would not be taken so aback