A PlanarFe Adventure

LearnLoveCode

JS Curry

SoMuchCurry

Now that I’m become firmly entrenched in the Javascript world, I figured it was high time that I read ‘The Good Parts’. Since I’m working in React, and since React is just just Javascript, it wasn’t too hard to get through, though one exercise took me a bit to get my head around - Curry.

First off, I always want to say ‘Currying’ because, it seems to me, that the whole point is to take a function an apply the Curry technique to it, thereby ‘Currying’ the function. It just feels like it needs an ‘ing’. But I suppose that is neither here nor there.

1
!(here || there)

The real sticking point for me has been finding examples of real-world applications for currying, something to which I might be able to relate, but these seem to be few and far between. StackOverflow let me down in this department, but Reddit seems to have gotten a bit closer(?) Almost every other example I ran across when reading about currying was some form of addition. Exciting, right? “Hey! Look! We built a function that you can pass a number to, and now every other time you call that function and pass it a number you will get back the sum of the very first and very latest number! Woot!”

Then, as an extra rub, there is something called ‘Partial Application’ which feels pretty similar but is technically different. So, let’s start off by looking at some definitions (Courtesy of Eric Elliott. Is it really courtesy when the person has no idea that you’re straight lifting their ish? Maybe there’s an informal definition. Note to self, google later):

Curry

A function that takes a function with multiple parameters as input and returns a function with exactly one parameter.

Partial Application

... a function that takes a function with multiple parameters and returns a function with fewer parameters. Partial application fixes (partially applies the function to) one or more arguments inside the returned function, and the returned function takes the remaining parameters as arguments in order to complete the function application.

So, the difference is that (again, thanks to Eric Elliott):

A curried function always returns another function with an arity of 1 until all of the arguments have been applied.

Whereas

A partial application may or may not have a predictable return type.

So let’s do this

So the idea is that we have a function that takes n arguments, but we don’t have all n arguments to hand at the moment.

… Fuck it, let’s add, ‘cause I’m not that creative and I can’t think of a better simple example at the moment.

1
2
3
  function add(a, b, c) {
    return a + b + c
  }

We have an add function that expects three arguments, but let’s say we only have one at the moment. We could save it to a variable, then save the second, then, when we have the third, finally call the function and pass along the three required arguments. Or, we can encapsulate the the data along with the logic into a deliciously curried dish.

1
2
3
4
5
6
7
8
9
10
11
  function add(a, b, c) {
    return a + b + c
  }

  function curriedAdd(a){
    return function(b) {
      return function(c) {
        add(a, b, c);
      }
    }
  }

We’ve created a closure. We can shove the variables a, b, and c into the closure, forget about them, and not have to worry about about mutating them somehow from outside out curried function.

To get the result out we just pass in our arguments one by one.

1
2
let adder = curriedAdd;
console.log(adder); // function curriedAdd(a){...}

So we have a function that accepts one argument, aka a function with an arity of 1, so we’re following the definition of curry. We then feed in the first argument and get back another function with an arity of 1, pass in the second argument, get back a function with arity = 1, then pass in the final argument and getting back the expected return value.

1
2
3
4
5
6
7
8
9
10
let adderA = adder(1);
console.log(adderA);
// function(b) {return function(c) {add(a, b, c);}}

let adderB = adderA(2);
console.log(adderB);
// function(c) {add(a, b, c);}

let adderC = adderB(3);
console.log(adderC); // 6

Or, more concisely

1
curriedAdd(1)(2)(3)

To Generalize

Now that we can curry a specific function, let’s figure out how to curry any function that takes multiple arguments.

We first define a function that takes another function as an argument, the function we want to curry and pass arguments to singly.

1
function curry(func) {  }

We know that we need to do something until all the required arguments to execute our function, say our add function are present. First thing is first, how many arguments are we waiting for?

1
2
3
function curry(func) { console.log(func.length) }

curry(add); // 3

We can make our curry function figure out how many arguments the function it is passed is expecting and change the behavior based on whether we have collected enough arguments to run the function.

1
2
3
4
5
6
7
8
9
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      console.log('Got all the arguments I need!')
    } else {
      console.log('Still waiting for more args')
    }
  }
}

And look! We get to play with ES6’s sweet little spread operator. Here, if curried is passed n number of arguments the spread operator collects them up into a single array, just in case we cheat and pass multiple arguments instead of one at a time as we should when currying (arity of 1 and all), then we check the length of the resulting array. If we have the requisite number of arguments to run our function, we do so.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function curry(func) {
  return function curried(...args) {
    // When a list of arguments is passed to curried
    // they are collected into an array
    if (args.length >= func.length) {
      // If all the arguments are provided,
      // pass `args` into the function to be curried and
      // 'spread' the `args` array into a list of argument
      // func(...args) becomes func(args[0], args[1], args[2]) and so on...
      return func(...args);
      // This is also equivalent to `func.apply(undefined, [arrayOfArgs])`
      // if you were to write in ES5
    } else {
      console.log('Still waiting for more args')
    }
  }
}

Spread Operator Syntax

For function calls:

1
  myFunction(...iterableObj);

For array literals:

1
2
3
4
5
  const miniArray = ['a', 'b', 'c']

  [...miniArray, 'simple', 'as', 1, 2, 3]

  => ['a', 'b', 'c', 'simple', 'as', 1, 2, 3]

Back to the show!

OK, so we know what to do if we’ve managed to collect all our arguments, but what if we don’t have them all yet? We want to return a function with an arity of 1 that takes it’s only argument, checks if it has enough arguments, executes the original function if it does, returns a function with an arity of 1 if it doesn’t… Sounds recursive to me!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func(...args);
    } else {
      return function(newArg) {
        console.log(
          `I have a new argument called 'newArg'` +
          `as well as access to 'args' from above`
        )
      }
    }
  }
}

From here we recursively call curried and pass it the new argument as well as all the arguments already collected.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func(...args);
    } else {
      return function(newArg) {
        return curried(...[...args, newArg]);
        // We spread `args` into an array along with the newArg
        // => [args[0], args[1], newArg]
        // Then spread this array into a list of separate arguments
        // which get passed to the `curried` function
      }
    }
  }
}

And cleaned up just a bit…

1
2
3
4
5
6
7
8
9
10
11
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func(...args);
    } else {
      return function(a) {
        return curried(...[...args, a])
      }
    }
  }
}

There! We created the ability to curry any function we like!

Another Use

But just using curry to pass in arguments sequentially as they are available is boring. Maybe applying curry for reusability might make a more interesting or compelling argument. M. David Green offers an example where multiple outputs require some shared data, but other pieces of data are different. Here we have a function that returns a greeting:

1
2
3
4
5
6
7
8
9
  var greetDeeplyCurried = function(greeting) {
    return function(separator) {
      return function(emphasis) {
        return function(name) {
          console.log(greeting + separator + name + emphasis);
        };
      };
    };
  };

Order matters here. You place the most common data as the first arguments, something like:

1
// greetDeeplyCurried(greeting)(separator)(emphasis)(name)

then pass in the unique arguments further down the line to improve reusability. The thing that is most likely to change from case to case should be last. Here it is the name.

1
2
3
4
  // greetDeeplyCurried(greeting)(separator)(emphasis)(name)
  var greetAwkwardly = greetDeeplyCurried("Hello")("...")("?");
  greetAwkwardly("Heidi"); //"Hello...Heidi?"
  greetAwkwardly("Eddie"); //"Hello...Eddie?"

greetAwkwardly forms a closure around the greeting, separator, and emphasis, and returns a function that can be reused for multiple names.

1
2
3
4
5
6
7
  var sayHello = greetDeeplyCurried("Hello")(", ");
  sayHello(".")("Heidi"); //"Hello, Heidi."
  sayHello(".")("Eddie"); //"Hello, Eddie."

  var askHello = sayHello("?");
  askHello("Heidi"); //"Hello, Heidi?"
  askHello("Eddie"); //"Hello, Eddie?"

Opinion

I don’t know if I’ll ever actually use curry. I haven’t run into it myself and it adds a bit of indirection. This exercise has, however, been useful in reinforcing the power of closures and, unexpectedly, made me take a closer look at the spread operator.

One issue with curry that has been nagging at me, mostly because of all the addition examples, is that they frame the problem as knowing that you need a certain number of arguments, but not having them immediately available. Curry lets us form a closure around the values and pass in the remaining arguments as they come along. But what if the order of the arguments matter, like with the greeting example above?

1
2
3
4
5
6
  // greetDeeplyCurried(greeting)(separator)(emphasis)(name)
  var sayHello = greetDeeplyCurried("Hello")(", ");

  var waitingForName = sayHello('.');

  waitingForName('Heidi'); //"Hello, Heidi." 

What if you don’t know in what order the arguments will arrive? It wouldn’t be very useful to have something like:

1
2
3
4
5
6
  // greetDeeplyCurried(greeting)(separator)(emphasis)(name)
  var sayHello = greetDeeplyCurried("Hello")(", ");
  // Waiting on an input and receive 'Heidi'
  var waitingForName = sayHello('Heidi');
  // Waiting on an input and receive '.'
  waitingForName('.'); //"Hello, .Heidi" 

Maybe you solve it with option parameters, which might look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function difference(a1, a2) {
  var result = [];
  for (var i = 0; i < a1.length; i++) {
    if (a2.indexOf(a1[i]) === -1) {
      result.push(a1[i]);
    }
  }
  return result;
};
// Lo-Dash and underscore both have array difference
// functions to use instead of the above

function curry(func, requiredArgs) {
  return function curried(options) {
    const optionArgs = Object.keys(options);
    const unfulfilledArgs = difference(requiredArgs, optionArgs);
    if (unfulfilledArgs.length === 0) {
      return func(options);
    } else {
      return function(newOption) {
        return curried({...options, ...newOption})
      }
    }
  }
}

const myFunction = function(args){console.log(args)}
const myCurriedFunction = curry(myFunction, ['hello','world']);
console.log(myCurriedFunction({hello:'world'})); // function(newOption){...}

myCurriedFunction({hello:'world'})({world:'hello'}); //{"hello":"world","world":"hello"}

Maybe this is a misuse of curry altogether and you skip all that to focus on reusability and ignore any kind of asynchronous cases where you don’t have tight control over the order the curried function receives arguments.

Sources:

JavaScript: The Good Parts by Douglas Crockford WatchMeCode: Currying Functions in JavaScript WatchMeCode: Build Your Own Curry Function in JavaScript Curry or Partial Application? by Eric Elliott Reddit- What is the advantage of currying? Currying vs Partial Application by Dave Atchley A Beginner’s Guide to Currying in Functional JavaScript by M. David Green