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:
0 1 2 |
nop = -> |
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:
0 1 2 |
id = (x) -> x |
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:
0 1 2 |
builtin = (f) -> nop.call.bind f |
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:
0 1 2 3 4 5 6 |
toArray = builtin Array::slice f = -> console.log toArray arguments f 1,2,3 # $ [1,2,3] |
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.
0 1 2 |
variadic = (as...) -> as |
It is quite useful for composition and point-free abstractions. A simple example:
0 1 2 |
variadic 1,2,3 #=> [1,2,3] |
apply
is the “normal” version of Function::apply
:
0 1 2 |
apply = (f, as...) -> f [].concat.apply([], as)... |
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:
0 1 2 |
apply Math.max, [1,2,3] #=> 3 |
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:
0 1 2 |
notF = (f) -> (as...) -> not f as... |
You could use notF
to implement odd
if you have even
:
0 1 2 3 |
even = (x) -> x % 2 is 0 odd = notF 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:
0 1 2 3 4 5 6 7 8 9 10 |
# not curried mul = (x, y) -> x * y mul(1,2) # curried mul = (x) -> (y) -> x * y mul(1)(2) # ---^ returns a function that expects one argument # --^ calls that function with `2` and returns the result |
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:
0 1 2 3 4 5 6 |
sum = (x, y) -> x + y # partially applied `sum` with `2` sum2 = (x) -> sum 2, x sum2 2 #=> 4 |
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:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
ncurry = (n, f, as=[]) -> (bs...) -> bs = as.concat bs if bs.length < n then ncurry n, f, bs else f bs... curry = (f) -> (as...) -> if f.length > as.length then ncurry f.length, f, as else f as... _ = {} # placeholder partial = (f, as...) -> (bs...) -> args = as.concat bs i = args.length while i-- if args[i] is _ args[i] = args.splice(-1)[0] f args... |
Update 11/14/14: The built-in bind
can also be used to build a generic curry
function:
0 1 2 3 4 5 6 |
curry = (f) -> (as...) -> if as.length < f.length f.bind [null, as...]... else f as... |
With curry
we can turn a function of any number of arguments into its auto-curried version, ie:
0 1 2 3 |
sum = curry (x, y) -> x + y sum(1)(2) #=> 3 |
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:
0 1 2 3 4 5 6 |
sum2 = partial sum, 2 sum3 = partial sum, _, 3 sum2 2 #=> 4 sum3 3 #=> 6 |
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:
0 1 2 3 4 5 6 7 8 |
add1 = (x) -> x + 1 by2 = (x) -> x * 2 # compose add1 with by2 add1by2 = (x) -> by2 add1 x add1by2 2 #=> 6 |
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.
0 1 2 |
compose = (fs...) -> fs.reduce (f, g) -> (as...) -> f g as... |
Now we can compose functions comfortably:
0 1 2 |
compose(by2, add1) 2 #=> 6 |
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
:
0 1 2 3 4 5 6 7 8 9 |
# flip function of 2 arguments flip = curry (f, x, y) -> f [y, x]... # flip function of 3 arguments flip3 = curry (f, x, y, z) -> f [z, y, x]... # flip variadic function flipN = (f) -> (as...) -> f as.reverse()... |
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.
0 1 2 3 4 |
# `compose` is a variadic function # so we need to use `flipN` sequence = flipN compose |
For example:
0 1 2 3 4 5 6 7 |
sequence(f(1), g(2), h(3)) arg # -----------------------^ a chain # ----^ call the chain on an argument # in Underscore _.chain(arg).f(1).g(2).h(3).value() |
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:
0 1 2 |
isType = curry (t, x) -> Object::toString.call(x).slice(8,-1) is t |
But isType
won’t check instances of objects, for that we need another function:
0 1 2 |
instanceOf = curry (ctor, x) -> x instanceof ctor |
Now that we got the types let’s implement some basic logic operations to be used with composition:
0 1 2 3 4 5 6 7 |
isF = curry (x, y) -> x is y isntF = notF isF gte = curry (x, y) -> y >= x lte = curry (x, y) -> y <= x gt = notF lte lt = notF gte |
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:
0 1 2 3 4 5 6 7 8 9 |
each = flip builtin Array::forEach map = flip builtin Array::map filter = flip builtin Array::filter fold = flip3 builtin Array::reduce all = flip builtin Array::every any = flip builtin Array::some reverse = builtin Array::reverse join = flip builtin Array::join |
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:
0 1 2 3 4 5 6 7 8 9 |
split = flip builtin String::split match = flip builtin String::match replace = flip3 builtin String::replace search = flip builtin String::search substr = flip3 builtin String::substr trim = builtin String::trim toUpper = builtin String::toUpperCase toLower = builtin String::toLowerCase |
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:
0 1 2 |
inArray = curry (x, xs) -> x in xs |
We could have implemented inArray
by borrowing the native indexOf
and using composition:
0 1 2 |
inArray = compose gt(-1), flip builtin Array::indexOf |
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:
0 1 2 |
slice = curry (i, j, xs) -> if j? then xs[i...j] else xs[i..] |
By currying slice
and using CoffeeScript’s destructuring features we can create most common functions for extracting items and cropping arrays:
0 1 2 3 4 5 6 7 |
first = ([x, xs...]) -> x last = ([xs..., x]) -> x rest = slice 1, null initial = slice 0, -1 take = slice 0 drop = partial slice, _, null, _ |
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:
0 1 2 |
reject = curry (f, xs) -> filter notF(f), xs |
Then we need flatten
, an essential array operation that will let us build other helpers with composition:
0 1 2 3 4 |
flatten = (xs) -> flat = [].concat.apply [], xs if xs.some Array.isArray then flatten flat else flat |
flatten
uses recursion to flatten deep nested arrays.
It is quite common to want to filter out duplicates, but to keep them too:
0 1 2 3 |
unique = (xs) -> xs.filter (x, i) -> xs.indexOf(x) is i dups = (xs) -> xs.filter (x, i) -> xs.indexOf(x) isnt i |
Finally an operation that can be performed in arrays that’s quite handy is zipping:
0 1 2 |
zip = (xss...) -> xss[0].map (_, i) -> xss.map (xs) -> xs[i] |
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:
0 1 2 |
zip [1,2], [3,4], [5,6] #=> [[1,3,5], [2,4,6]] |
Zipping can also be performed with a callback to do something with the zipped items:
0 1 2 |
zipWith = (f, xs...) -> map partial(apply, f), apply zip, xs |
Using zipWith
we can do the following:
0 1 2 3 |
add = (x, y) -> x + y zipWith add, [1,2], [3,4] #=> [4,6] |
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:
0 1 2 3 4 5 6 |
forOwn = curry (acc, f, obj) -> i = 0 for own k, v of obj acc = f [acc, k, v, i++]... acc |
With forOwn
we can sum the values of an object for example:
0 1 2 3 4 5 6 7 8 |
obj = {a:1, b:2, c:3} sum = (acc, key, val) -> acc += val acc forOwn 0, sum, obj #=> 6 |
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:
0 1 2 |
pairs = forOwn [], (acc, k, v) -> acc.concat [[k,v]] |
And then use pairs
like:
0 1 2 |
pairs {a:1, b:2} #=> [['a',1], ['b',2]] |
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:
0 1 2 3 4 |
unzipObject = forOwn [[],[]], (acc, k, v, i) -> acc[0][i] = k; acc[1][i] = v acc |
For example:
0 1 2 3 4 |
obj = {a:1, b:2} keys = unzipObject(obj)[0] #=> ['a', 'b'] values = unzipObject(obj)[1] #=> [1, 2] |
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'
:
0 1 2 3 4 5 |
pluck = curry (x, xs) -> String(x).split('.').reduce (acc, x) -> if x of Object acc then acc[x] else undefined ,xs |
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:
0 1 2 3 4 |
pluck 'a', {a:1, b:2} #=> 1 pluck 'a.b.c', {a:{b:{c:1}}} #=> 1 map pluck('length'), ['one','two','three'] #=> [3,3,5] |
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:
0 1 2 |
pluckR = curry (x, xs) -> (xs while xs = pluck x, xs) |
The DOM is a classic example of recursive plucking:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
# given: # <ul> # <li>A</li> # <li>B</li> # <li>C</li> # </ul> el = document.querySelector 'li:first-child' #=> <li>A</li> # will return all next siblings nextAll = pluckR 'nextElementSibling' nextAll els #=> [<li>B</li>, <li>C</li>] |
With our plucking and iteration object helpers we can implement many other functions, here’s a few:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
size = compose pluck('length'), Object.keys # transforms an array like [a, 1, b, 2] # into and object {a:1, b:2} toObject = (xs) -> xs.reduce (acc, x, i) -> acc[xs[i-1]] = x if i % 2 acc ,{} # the opposite of `unzipObject` zipObject = compose toObject, flatten, zip # Check if a property is defined in an object # using `pluck` to be able to search by path has = compose notF(isType('Undefined')), pluck |
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:
0 1 2 3 4 5 6 7 8 9 10 |
compact = filter Boolean flatMap = flip compose flatten, map union = compose unique, flatten, variadic intersection = compose unique, dups, flatten, variadic difference = (xs, as...) -> reject inArray(unique(flatten as)), xs partition = curry (f, xs) -> [filter(f, xs), reject f, xs] words = split ' ' unwords = join ' ' # and more... |
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.
Awesome. I need to read this again.
> gt = curry (x, y) -> y > x
> lt = notF gt
that’s wrong, gt == notF lte, lt == notF gte
Thanks for pointing that out, I missed it.
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.
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.
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!
You define an
id
function, but you don’t seem to use it at all, or am I missing something? Very interesting post otherwise.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 🙂