/ functional programming

Curried Functions and Point-Free Programming

Ordering the arguments of our functions isn’t something that we often spare too much thought about, but after experimenting with Elm recently (it’s great by by the way!) the benefits have become clearer. Like many functional programming languages, Elm’s functions are automatically curried – if we have a function with two arguments and only supply one, then a new function that accepts the remaining argument is returned. In Elm this looks like:

add : Int -> Int -> Int
add number1 number2 =
    number1 + number2

addFive: Int -> Int
addFive =
    add 5
    
add : Int -> Int -> Int
add number1 number2 =
    number1 + number2
 
addFive: Int -> Int
addFive =
    add 5

The addFive function is created by calling the add function with a single argument, which returns another function expecting only a single argument. The equivalent JavaScript would be:

const add = number1 => number2 =>
    number1 + number 2;

const addFive = add(5);

const add = number1 => number2 =>
    number1 + number 2;
 
const addFive = add(5);

What’s the point of all this? It’s not very clear with the above contrived examples, but it really comes into its own when we start to use it within JavaScripts array functions, such as map. Let’s try a common approach to mapping an array, adding 5 to each element.

const arr = [1, 2, 3, 4, 5];

const arrPlusFive = arr.map(el => el + 5);
// [6,7,8,9,10]

Using a curried add function like we defined previously, we could instead have written:

const arr = [1, 2, 3, 4, 5];

const arrPlusFive = arr.map(add(5));
// [6,7,8,9,10]

Personally I feel this is already getting towards a nicer more declarative piece of code, that more accurately reflects our intent, rather than the procedural steps we want to perform. A further advantage will come when we find ourselves needing to perform another similar transformation – it is trivial to see how we can alter our code to add 10 to each array element instead of 5.

This is an example of a style known as “Point-Free” – we do not refer to the data upon which we are operating at the point of operation. Perhaps another example makes this clearer – let’s imagine we want to log every element of an array to the console; we can achieve this, again, with a map function:

[1,2,3,4,5].map(element => console.log(element));

Note that the lambda function passed to map is a function of arity one (one argument), that passes its argument to console.log. Is there another function that does the same thing as this? Yes there is! It’s console.log itself – we can rewrite the above to:

[1,2,3,4,5].map(console.log)

Once this pattern is recognised, it is astounding how many places within your code can be simplified in this manner. Again, this makes code more declarative and less procedural with a corresponding improvement in clarity of intention.

As an aside, running this will actually print:

1, 0, [1,2,3,4,5]
2, 1, [1,2,3,4,5]
etc.

This is because map actually calls the mapping function with three arguments each time:

mapFunction<T, U> = (element: T, index: number, collection:T[]) => U;

Hopefully it is somewhat clear the benefits that currying functions and point-free style can bring to our code. However, is there a nice way to implement this in JavaScript so that we can gain these benefits without radically altering all of our call sites. Imagine a function addFourNumbers that takes 4 numbers, adds them all together and returns the result. Can we design an implementation so that the following are all equivalent:

addFourNumbers(1,2,3,4);
addFourNumbers(1,2,3)(4);
addFourNumbers(1,2)(3,4);
addFourNumbers(1)(2,3)(4);
addFourNumbers(1)(2)(3)(4);
// You get the idea...

I probably wouldn’t ask the question if the answer wasn’t yes… so let’s see how to do it:

const addFourNumbers = (...args) => {
    if  (args.length < 4) { 
        return (...remainingArgs) => addFourNumbers.apply(this, [...args, ...remainingArgs]);
    }
    return args.reduce((a,b) => a + b, 0);
}

The function is short but encapsulates a number of somewhat tricky concepts. If there are four arguments supplied then we sum them and return it. If there are less than four arguments then we need to return a new function – this returned function accepts more arguments, then uses apply to call addFourNumbers again with both the original arguments (args) and the new ones (remainingArgs).

Essentially we have a recursive function with a base/terminal case when the number of arguments reaches four.

This works, but it’s going to be a bit of a pain to write all of our functions this way – is there a way that we can use currying on any function we like? We can take advantage of the the function.length property – which tells us the number of arguments to a function (its arity) – to write a function that returns a curried version of any other function:

const curry = fn => (...args) => {
    if (args.length < fn.length) {
        return (...restArgs) => curry(fn)(...args, ...restArgs);
    }
    return fn.apply(this, args);
};

This works similarly to the previous example, and can be used like this:

const simpleAddFour = (first, second, third, fourth) => {
    return first + second + third + fourth;
};

const addFour = curry(simpleAddFour);

addFour(1)(2,3)(4); // 10
addFour(1,2,3,4);   // 10
Curried Functions and Point-Free Programming
Share this

Subscribe to Developing Thoughts