Implementing Curry And Compose

In a previous article I extolled the virtues of curry and compose.

They're great if you're in a position to import a library like ramda but what if you're not? What if you're, just for the sake of argument, doing some coding challenges at freecodecamp.com which don't allow you to import your favorite libraries? :)

Worry not. Here are the implementations of curry and compose with a breakdown of each.

First, let me introduce a helper function that takes something array-like (like, for example, a function's arguments) and converts it to an array:

function to_array(array_like) {  
    return Array.prototype.slice.call(array_like)
}

curry

Here's the code:

function curry(func) {  
    var arity = func.length
    return function f1() {
        var f1_args = to_array(arguments)
        if(arguments.length >= arity)
            return func.apply(undefined, f1_args)
        return function f2() {
            var f2_args = to_array(arguments)
            return f1.apply(
                undefined, f1_args.concat(f2_args))
        }
    }
}

curry returns a function (f1) that returns one of two things.

If the length of the new function's arguments is the same or greater than the original function's arity (arity being a fancy word to describe the number of arguments a function expects) the result is returned just as if the caller were calling the original function.

If the length is less, another function (f2) is returned, one that returns the result of calling another f1 with the original f1 call's arguments and f2's arguments combined.

The second bit, f2, is how we get a pre-loaded function by calling a curried function with only some of its arguments. You can see that f2 will keep getting returned recursively until we get all the required arguments.

compose

Here's the code:

function compose() {  
    var functions = to_array(arguments).reverse()
    return function () {
        var result =
            functions[0].apply(
                null, to_array(arguments))
        return reduce(
            function (result, func) {
                return func(result)
            },
            result,
            functions.slice(1))
    }
}

Here's the breakdown:

compose takes its arguments, which should be a list of functions, turns them into an array called functions, and reverses their order. The order is reversed so that the first element in the array is the first function we call, the second element the second function and so on.

compose then returns a function that takes its arguments applies the first function in functions to those arguments. This return value is the initial value of an accumulator in a reduce call.

The result of this reduce call is what is returned by the function returned by compose. It simply calls each of the remaining functions in functions in succession and passes the return value as the new accumulator value.

In case you're wondering, I implemented reduce like so:

var reduce = curry(function (func, acc, arr) {  
    return arr.reduce(func, acc)
})

That's it! These functions are pretty powerful but they're not really that much code.

Looking for a software developer?