Chapter 03 – Currying & Composition

Chapter 03 - Currying & Function Composition

Introduction

Do you remember the movie Inception? Yeah, that DiCaprio flick that was the first mainstream movie since The Matrix (the first Matrix…let’s just ignore the other two…) that made people’s heads spin. Well, I hope you liked Inception because currying is a bit like Inception but thankfully quite a bit more shallow (though far less entertaining). That’s the first topic we will be tackling. After that, we’ll take a very interesting dive into function composition. Think of it like a game of telephone but hopefully without the massive error rate :/.

spinning top

OK, so what is currying?

Currying is generally considered an advanced method of working with functions. In a nutshell, currying is simply a way of transforming a function that is typically called with multiple parameters (arity) into a function that is callable with fewer parameters in succession. Yeah, that was awkward. Let’s take a look:

// Lets say we have a sum()
function sum(a, b) {
  return a + b;
}

// Which is called like so:
sum(5, 10); // 15

// But we can curry this function like so:
function curry(f) {
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

let curriedSum = curry(sum);

// Then we can call sum like this:
curriedSum(5)(10); // 15 

Well, that’s great but…why would I want to do this? Currying allows us to do two pretty useful things:

  1. It allows us to create a partial function application (I like the term convenience function).
  2. It lets us shortcut functions that use a recurring parameter.  

Let’s take a pretty simple yet helpful example to understand this. Let’s say that we want to keep track of a list of logs throughout our application. Now, we could do this in several ways but let’s take a curried look:

// Let's define our main log function
function log(date, importance, message) {
  alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);
}

// A normal application might look like:
log(new Date(), "DEBUG", "some debug message");
// -> [12:30] [DEBUG] some debug message

// But if we curry the function using lodash's curry:
let curryLog =_.curry(log)

// We can now use it as a convenience function:
let logNow = curryLog(new Date());
// logNow is a partial function where the first param is the date

// Completing the function application:
logNow("ERROR")("User not found");

// And the result will be:
// -> [12:30] [ERROR] User not found

// And the nice thing is that we can keep using it:
logNow("SUCCESS")("User created");
// -> [12:30] [SUCCESS] User created 

That’s a pretty handy trick! The “logNow()” partial function has a fixed first argument for all subsequent calls. This fixed argument is an example of a shortcut to avoid a recurring parameter. We can use this for things like logging but also to allow for a nice convenience function for things like DB connections, query construction, or anything else that is constructed in a set number of parts.

In the example above, I used the lodash library’s curry function. This implementation allows you to maintain the original function’s parameters (a, b, c) or use the successive function call method (()()()). Just for reference sake, here is an implementation of the curry transformation function:

function curry(func) {
  // Return a wrapper function with arbitrary arity and spread those into the function
  return function curried(...args) {
    // If the arity passed matches the arity of the function being curried...
    if (args.length >= func.length) {
      // Simply return the function but bind the context and call it with the passed args.
      return func.apply(this, args);
    } else {
      // If the arity is different...
      return function(...args2) {
        // return a new function that calls the curried wrapper.
        return curried.apply(this, args.concat(args2));
      }
    }
  };
} 

How it Works

Currying works thanks to JS’s use of closures. Each subsequent function closes over the previous and these actions effectively keep the previously passed parameters alive and in scope for the final function’s execution block.

// From the above example
function curry(f) {
  // f is in scope
  return function(a) {
    // f and a are in scope
    return function(b) {
      // f, a, and b are in scope
      return function(c) {
        // f, a, b, and c are in scope
        return f(a, b, c);
      }
    };
  };
} 

Function Composition

Function composition is one of those abstract concepts that once you get introduced to it you think, “hmm, that seems simple…but why does it even matter?” As with a lot of functional programming concepts, you’re right, it kind of doesn’t matter in the sense that you don’t really need it. You can write programs without it and they would, arguably, be just as good both programmatically and conceptually. So let’s get into what it is, why it might matter to you, and how to implement it if you think it has value.

Mathematically, function composition is simply an operation that takes two functions and composes them such that the result is another function that maps a value across both functions in a particular order (typically right to left or what might be called folding right). Great, so that definition was entirely useless. Let’s go to the examples!

Let’s say we have two functions f() and g(). Let’s also say that running f(X) gets you a value Y and running g(Y) gets you to value Z. From that logic, the flow becomes X->Y->Z. Now, let’s pull in some of this compose funkiness. When you compose the two functions f() and g() you get 

g(f(X)) = Z

 Do you see what we just did there? The result of f(X) is Y, which gets plugged directly into g() and the final result is the value Z. That’s largely it. That’s function composition at a basic level! Now, of course, this gets more complicated. Let’s first cover one more concept before we dive into the practical side of all of this and why you might want to use this craziness.

Folding Left or Right...Order Matters...Sometimes

Within the functional programming space, there is a concept of folding. Folding functions are known to most people by other names, most notably “reduce” or “aggregate” higher-order functions. The basic idea is that a fold walks a recursive structure and tracks/aggregates operations to result in a final output. It does this by passing an aggregator into each function in the chain until there are no more functions to process and the final aggregation value is returned. 

Now, in languages like Haskell, you have core HOFs like foldl() and foldr() [left and right respectively]. These basically let you decide on the direction of the fold. So for instance, if you wanted to process an array [1, 2, 3, 4, 5] through an addition function, a foldl() would process this as 1+2+3+4+5 = 15 whereas a foldr() would be 5+4+3+2+1 = 15. In this case, the operation is associative so order doesn’t matter. However, in most cases where we have functions instead of integer values, order most certainly matters.

Let’s go to some examples!

Let’s say that we have a result set of some kind of search and we want to format and filter the results for easy display on the front end from an interpretive perspective, we might do something like this:

const results = [
    {
        name: 'Brandon',
        age: 35,
        zipcode: 55555
    },
    {
        name: 'Julie',
        age: 33
    },
    {
        name: 'Tyler',
        age: 15,
        zipcode: 55555
    },
    {
        name: 'Tyler',
        age: 22,
        zipcode: 11111
    }
]

// We only want entries with zip code.
const withZip = _.filter(results, 'zipcode');

// Now, we want to format it so it is displayed how we want it.
const formattedResults = withZip.map( r =>
    {
        r.formattedString = `${r.name} - ${r.zipcode}`;
        return r;
    }
);

// Finally, we want to sort them
const finalResult = _.sortBy(formattedResults, 'formattedString'); 

Now, this looks pretty normal. We’re using processing each step and it does everything we need in a pretty straight forward manner. Now, let’s try composing this using lodash’s compose.

// Pull in the functional programming version of lodash.
fp = require('lodash/fp');

const getFinalResult =  fp.compose(
    fp.sortBy('formattedString'),
    fp.map(r => ({
        ...r,
        r.formattedString = `${r.name} - ${r.zipcode}`;
    })),
    fp.filter('zipcode')
);

const finalResult = getFinalResult(results); 

Well now, what do we have here? At first glance, this may seem a bit more complicated and I certainly wouldn’t fault you for thinking that. But let’s break this down a bit. First, you’ll notice that we’re using the fp library of lodash. This library makes all the lodash methods operate functionally. All methods are immutable and they are auto-curried to accept the data parameter as the final function call. Additionally, all methods have a fixed arity which means that they all take an exact number of arguments. You should check out the documentation linked above!

Anyway, back to our code. Let’s ignore the fp.compose() for a minute and focus on the three arguments. If you remember what we said about currying above, this is a perfect example of how it can be useful. Each of these arguments is a method call that has an arity of two and returns a partial function application. We’re only calling each with a single argument so each is there waiting to receive its data payload! And wouldn’t you know it, that’s exactly what the fp.compose() does. Sounds pretty similar to a regular reduce(), doesn’t it?

When we call getFinalResult(results) we pass in the unfiltered and unsorted array of objects. The fp.compose() method takes the results array and calls fp.filter(‘zipcode’)(results). It then takes the result of that and calls fp.map(fn)($filterResults). Then that result is called with fp.sortBy(‘formattedString’)($mapResults). Pretty freaking neat right?

mind blown

Now, the thing to remember here is that fp.compose() is an alias to _.flowRight() and folds from the right which means that the last function gets executed first and it works up the chain. This is the natural progression if you were to convert something like:

const finalResult = _.sortBy('formattedString', _.map(r=>({...r, r.formattedString = `${r.name} - ${r.zipcode}`}), _.filter('zipcode', results))); 

References

https://blog.bitsrc.io/understanding-currying-in-javascript-ceb2188c339

https://javascript.info/currying-partials

https://hackernoon.com/function-composition-with-lodash-d30eb50153d1

https://wiki.haskell.org/Fold

https://en.wikipedia.org/wiki/Function_composition