James Robb

Posted on • Updated on

Currying

In mathematics and computer science, currying is a way of splitting a function which takes multiple arguments into a sequence of functions that take a subset of those arguments until all are provided and can be used to execute the original function.

For example if we had a function like this:

``````function createPerson(name, age, occupation) {
return {
name,
age,
occupation
};
}

console.log(
createPerson("James", 26, "Senior Software Engineer")
);
``````

We could also make something like this to generate the same output:

``````function createPerson(name) {
return function(age) {
return function(occupation) {
return {
name,
age,
occupation
};
}
}
}

console.log(
createPerson("James")(26)("Senior Software Engineer")
);
``````

This is useful in scenarios where you only have part of the data available for execution and wish to partially apply incoming data until all requirements are fulfilled. This is what makes currying and partial application two very related subject matters.

In this article we will write a higher order function to auto-curry any other function for us to avoid nested `return function` statements like we saw in the example above.

Tests

``````function add(left, right) {
return left + right;
}

function createPerson(name, age, occupation) {
return {
name,
age,
occupation
};
}

describe("Curry", () => {
it("Should throw for invalid parameters", () => {
expect(() => curry("string")).toThrowError(TypeError);
});

it("Should run as normal if all arguments are presented", () => {
expect(sum).toBe(3);
});

it("Should run if values are passed in via consecutive calls", () => {
expect(sum).toBe(3);
});

it("Should run if values passed in consecutive calls of varying length", () => {
const curriedCreatePerson = curry(createPerson);
const person = curriedCreatePerson("James")(26, "Senior Software Engineer");
expect(person).toEqual({
name: "James",
age: 26,
occupation: "Senior Software Engineer"
});
});
});
``````

These tests are written in Jest and cover 3 primary cases for our auto-currying function:

1. If all values are present will the function run as if it hasn't been curried?
2. If I present the values in consecutive function calls, will it return as expected once all necessary values are supplied?
3. If I have multiple parts of data available to apply, can I do so without more function calls than is necessary?

In short, you can call the curried version of the function as often or as little as you like so long as the values from each call equate eventually to a minimum of those required by the function that was curried.

Implementation

This implementation will introduce some oddities of the JavaScript language, specifically that when it comes to functions, JavaScript allows us to access a `length` property representing the amount of arguments required for that function.

For example if I were to run the following:

``````function add(left, right) {
return left + right;
}

``````

We would see an output to the console of `2` since we require the `left` and `right` parameters to be present.

This is interesting and weird in my opinion although I am sure some others think this is totally a normal feature to have but I don't know... Anyway, I recommend reading the MDN article on the function length property to understand why and how this works.

For our `curry` higher order function function we use this property which is why I thought it relevant to bring up for those who may not be aware of it.

Anyhow, without further ado, here is the implementation I have chosen to go with for the `curry` higher order function:

``````/**
* @function curry
* @description A function which can curry any another function
* @param {Function} fn - The functions to curry
* @returns {(Function|*)} A function to apply the remaining required aruments to or the return value of the provided `fn` when enough arguments are eventually provided
*/
function curry(fn, ...parameters) {
if (typeof fn !== "function") {
throw new TypeError(`Parameter 1 must be of type Function. Received: \${typeof fn}`);
}

if (parameters.length >= fn.length) {
return fn(...parameters)
}

return (...args) => curry(fn, ...parameters, ...args);
}
``````

In short, our implementation utilises the `length` propery to check if the `parameters` provided to the `curry` function equate to that of the requirements of the `fn` function. If the requirements were fulfilled, it runs the `fn` function with the `parameters` that were provided and returns it's value.

If on the other hand there are not enough `parameters` available, a new function is returned which takes a set of `args`. When this new function is executed, we recursively run the `curry` function again passing in any previously provided `parameters` and the new incoming `args` as the new iterations value of the `parameters` argument. From here the cycle either breaks or is recursed again until all values that are required are provided and the `fn` that was originally provided can be executed and it's return value returned.

Conclusions

Currying is a relatively simple thing once you wrap your head around it and plays a relatively big role in Functional Programming due to it's ability to easily achieve partial application and split larger actions into smaller ones.

Currying is also a relatively common concept and so understanding how it works in your language of choice won't hurt, for example in PHP I would rewrite the JavaScript implementation above, like so:

``````function curry(string \$fn, ...\$parameters) {
\$reflector = new ReflectionFunction(\$fn);
\$requiredArgs = \$reflector->getNumberOfParameters();

if(count(\$parameters) >= \$requiredArgs) {
return call_user_func(\$fn, ...\$parameters);
}

return function(...\$args) use (\$fn, \$parameters) {
return curry(\$fn, ...\$parameters, ...\$args);
};
}
``````

The code is almost identical but we need to use a few different language specific features such as the `ReflectionFunction` class that PHP gives us or that functions are passed as string references for example but generally speaking this is the same code with only some minor differences. We can even run the PHP example basically the same way we do the JavaScript version, like so:

``````function add(\$left, \$right) {
return \$left + \$right;
}

echo \$result; // 3
``````

How might you implement this in your language of choice? Let me know in the comments below!

Jacob Baker

Slightly off-topic, but it is way to close to dinner time for this to keep going to the top of my feed and showing me a huge picture of a curry!

James Robb

I felt the same and actually just ordered food not that long ago π

Greg Fletcher

Nice! Do you often use functional programming in your normal daily programming?
I personally prefer functional programming when possible but I'm always interested to hear what people like about it For me, a couple of the benefits of functional programming are the readability of the code and also the removal of boilerplate.

James Robb

I try to use it as often as possible but working with different clients means that sometimes their systems are in OOP languages or some other paradigm and so using FP isn't always possible. It is something I find easier to reason with though personally since I aim to break any task down to the basics and this maps well with FP in my opinion. All I need to do is compose each task/action together to have the desired outcome and so using something like the compose function or pipe function is very useful in such scenarios.

I had commented on another currying article on Dev.to over here, dev.to/eljayadobe/comment/o3p

Currying in JavaScript is "meh".
Currying in C++ is "omg, ow ow ow".
Currying in F# is "ahhh, nerdvana".

James Robb

Absolutely π

Eric Ahnell

I am very interested in adopting functional programming, for one simple reason: functions, being free of side effects, are easier to reason about and verify. The type of things I create benefit greatly from this attribute!

James Robb

Agreed, it can give you a lot of freedom but other paradigms bring benefits of their own, it's really a balance always but if you keep functions simple and work them together logically it can work out pretty well for all involved.

Eric Ahnell

I agree! I donβt think switching exclusively is beneficial enough to do for exactly the reasons you stated, but I would benefit from using the ideas where they make sense.