Functional CoffeeScript for the impatient

So you’re using CoffeeScript, great! But are you exploiting all its features? Functional CoffeeScript is fun, but you need some helpers. In this tutorial we’ll cover the most essential functional helpers and techniques to build a solid foundation for your next project. Let’s keep it DRY!

Foundation

To begin we’ll need a few essential functions. Our very first helper is a nop (or noop) or empty function:

An empty function does nothing, duh. But it is useful nonetheless.

Another helper we’re going to need is an identity function, that returns the argument given to it:

We’ll see some use cases of nop and id next.

The builtin function let’s us turn any native function into a regular function:

We make use of nop to access the generic call method of functions and then bind f (the given native function) to the this value. What this will do is give us back a function that will generate “normal” functions out of “prototype functions”, for example:

toArray isn’t very useful in CoffeScript to get a real array from arguments because we have a rest operator ..., but it is useful nonetheless for other operations, such as transforming DOM collections into arrays for example. We’ll keep it around for later.

The variadic helper returns back the arguments as an array when called.

It is quite useful for composition and point-free abstractions. A simple example:

apply is the “normal” version of Function::apply:

We make sure that the arguments are flattened one level, otherwise we’ll end up with a nested array and the applied function won’t work as expected. We can use apply like:

Higher-order and currying

As you probably know, CoffeeScript functions are objects, with properties and methods, that you can pass around, just like any other object.

With functions as first class citizens we get higher-order functions; the essence of functional CoffeeScript. A higher-order function takes one or many functions as arguments or returns a function. This is the way to do composition, and make your code more declarative.

Our first look at higher-order functions is a very useful helper by the name of notF, which takes a function that returns a boolean, and gives you a function that negates the returned value:

You could use notF to implement odd if you have even:

When you got these powers you need to use them wisely, and for that you need curried functions and partial application.

Currying is a simple concept, where you transform a function that takes two or more arguments into a function of one argument that keeps returning a function of one argument until all arguments have been passed, for example:

A partially applied function is one that has been pre-filled with any number of arguments, but that still has one or more arguments left to be passed, for example:

These two concepts, “currying” and “partial application”, aren’t incredibly complex and they are very useful in practice, but not as easy to abstract for re-use.

We’ll use curry and partial a lot in this tutorial. Many functional libraries already include these helpers but here’s a possible implementation in CoffeeScript:

Update 11/14/14: The built-in bind can also be used to build a generic curry function:

With curry we can turn a function of any number of arguments into its auto-curried version, ie:

From now on remember that we always need to curry functions of two or more arguments as it will improve code re-use, and simplify composition.

With partial we can apply arguments in any order, using _ as a placeholder for arguments to come:

In conclusion, understanding the concepts of currying and partial application is key to using these helpers, even if you don’t understand the implementation.

Composition

Composition in CoffeeScript is easy:

But it’s repetitive; this can be abstracted. You can think of composition as the reduction of many functions to a single function, by calling them in order on the given arguments.

Now we can compose functions comfortably:

Sometimes functions play badly with composition because the arguments are in the wrong order. In such cases you need a helper to switch the arguments around, flip:

When flipping a variadic function (function that takes any number of arguments), it is important to remember that currying won’t work as expected, and that you should use partial instead to avoid issues. curry expects a function with a finite number of arguments.

Underscore has a helper called _.chain that lets you write code in a sequence, so it’s easier to read and maintain. With what we have so far we can implement this concept very easily, all we need to do is flip compose so it reads left to right and top to bottom.

For example:

Types and logic

CoffeeScript’s typeof operator sucks, let’s be honest. But luckily we can get the type of any object using its internal string representation, which returns something like [object Type], and what we need is the type:

But isType won’t check instances of objects, for that we need another function:

Now that we got the types let’s implement some basic logic operations to be used with composition:

Note that the arguments are flipped in the comparison so we can curry these functions nicely. Remember that the receiver of the operation must always come last, so we can abstract y away from the code.

Arrays and strings

We can implement native array and string methods using the builtin helper, but we need to flip the arguments around to play nice with composition; this is where Underscore got it wrong.

Instead of func(xs, f) (callback last), we need func(f, xs) (callback first), so we can abstract xs away with composition. Otherwise we would need to use partial application everywhere, and that’s not fun nor readable. Underscore provides _.chain to mitigate this problem, but it’s still a bit verbose.

Let’s start with arrays:

Note that if the method has one argument you use flip, but if it has two arguments you use flip3, that’s because the receiver counts as one more argument. In xs.reduce(f, acc), by doing fold = flip3 builtin Array::reduce we get a function like fold(acc, f, xs), great for currying and composition.

We can do the same with strings:

You can see that methods that don’t take arguments, such as trim don’t need to be flipped, because the function that we get back from builtin has only one argument, the receiver.

To check for items in an array we’ll use CoffeeScript’s in operator wraped in a curried function:

We could have implemented inArray by borrowing the native indexOf and using composition:

But it isn’t necessarily more readable to do it this way, so we’ll just keep the first version.

Now let’s slice those arrays! As Array::slice can take an optional argument we won’t use builtin. We’ll make a function that plays nice with partial application by adapting the logic depending on a nullable argument:

By currying slice and using CoffeeScript’s destructuring features we can create most common functions for extracting items and cropping arrays:

We’re done with built-in functions and wrappers. Now we need to implement some common helpers found in Underscore and other libraries to work with arrays and collections.

First let’s create the inverse of filter so we can filter elements out. We can simply negate the filter function to achieve this:

Then we need flatten, an essential array operation that will let us build other helpers with composition:

flatten uses recursion to flatten deep nested arrays.

It is quite common to want to filter out duplicates, but to keep them too:

Finally an operation that can be performed in arrays that’s quite handy is zipping:

A zip takes any number of arrays an combines it’s elements by zipping each element in same index position from each array, for example:

Zipping can also be performed with a callback to do something with the zipped items:

Using zipWith we can do the following:

Objects

Objects are not as well abstracted for iteration as arrays are in CoffeeScript. Other than the of and own operators there aren’t any built-in functions to reduce an object, or get an array of values for example.

To fold an object we can loop and keep track of an accummulator that it’s passed in every call to the given callback. This is like Array::reduce for objects:

With forOwn we can sum the values of an object for example:

Keep in mind that the order of properties of an object isn’t guaranteed by the ECMA spec and it’s up to the vendor to implement it. Although in most browsers and NodeJS it does output the properties in order most of the time, but don’t count on it. If your operation is commutative then it doesn’t matter, like in the case of addition, or multiplication.

We can curry forOwn to create a helper to transform an object into an array of key-value pairs:

And then use pairs like:

Another operation we can do by currying forOwn is unziping an object, that is taking the keys in one array and the values on another:

For example:

Extracting a property is a very common operation on objects. Underscore has two functions for doing this: _.pluck and _.property. But with currying we can implement this functionality more efficiently. First, instead of two functions we’ll have just one that can be partially applied to a mapping operation if needed. Second, instead of taking a single property we can lookup full paths in objects by using a string like 'a.b.c':

We use Object acc to make sure the of operator works on literals like strings, which aren’t really objects, but primitives that get wrapped in an object when used.

Now we can use it with single properties, full paths and mapping:

Another not so common, but still useful operation on objects is to lookup properties recursively, which we can do using a comprehension and our previous pluck helper:

The DOM is a classic example of recursive plucking:

With our plucking and iteration object helpers we can implement many other functions, here’s a few:

Conclusion

With a handful of helpers we can ease the transition into functional programming with CoffeeScript. Currying, partial application and composition are key to building higher abstractions, and writing beautiful declarative terse code.

The possibilities are now endless, with a solid foundation the good one liners come naturally, take a look:

For more check out Essential.js, a library built in CoffeeScript using all of these concepts.

For even more functional goodies check out LiveScript and its official library Preludels.

8 Responses

  1. Eugene Mirotin May 19, 2014 / 6:45 am

    > gt = curry (x, y) -> y > x
    > lt = notF gt

    that’s wrong, gt == notF lte, lt == notF gte

    • Cedric Ruiz May 19, 2014 / 11:37 am

      Thanks for pointing that out, I missed it.

  2. Gleb August 20, 2014 / 1:43 pm

    Great article, thanks a lot. With all great little JavaScript functional libraries (like your essential.js, wu.js, Ramda, even lodash and lodash-contrib), is there something about CoffeeScript that gives it an advantage? Aside from terse function syntax, the only thing that appeals to me is function.call.bind feature. And I love CoffeeScript myself.

  3. Nick Cox August 27, 2014 / 4:46 am

    So, this is phenomenal. But I’m not patient. In fact, I’m very patient. Like, if you fleshed this post out with details and explanation and made it into an eBook, I’d easily pay $15 or so. Just saying. I’ve been struggling to widen my OO mindset with functional style, and this helped me see what was possible, but it was a bit over my head. But I feel like with a bit of explanation, this would really clarify a lot of core concepts.

    • Cedric Ruiz November 4, 2014 / 11:42 pm

      I am indeed thinking about making this post into something more detailed, maybe a book, or maybe expand it into multiple posts for each concept. Thank you for your feedback!

  4. Thomas April 27, 2015 / 2:22 pm

    You define an id function, but you don’t seem to use it at all, or am I missing something? Very interesting post otherwise.

    • Cedric Ruiz May 4, 2015 / 10:56 pm

      Yes, you are right, it is not used in the code, I guess it is such an essential function that I couldn’t leave it out 🙂

Leave a Reply to Thomas Cancel reply

Your email address will not be published. Required fields are marked *