For me, one of the things that makes JavaScript so interesting is the functional aspect of the language. From the beginning, functions have been first-class citizens in the JavaScript world. This makes it possible to write elegant and expressive code that can be easily composed together in multiple ways.
However, just by having the ability to do some functional programming does not auto-magically result in functional programming. Ramda.js is a quite popular library (having over 4k stars on GitHub) that we can use to help us getting started with functional programming using JavaScript.
Getting Started
To fully use Ramda.js we should get used to its benefits by creating a small Node.js project. We can simply install it via the Node Package Manager (npm).
npm install ramda
Usually, we will simply import the library's functionality into the namespace R
. This way all calls to Ramda's methods will have an R.
prefix.
var R = require('ramda');
Of course nothing prevents us from using Ramda.js in front-end code. In the browser, we only need to include a proper path to a copy of the library. This may be as simple as the following HTML snippet.
<script src="ramda.min.js"></script>
Ramda.js does not use any DOM or Node.js specific features. It is only a language library / extension, and builds upon structures and algorithms already exposed by the JavaScript runtime (as standardized in ECMAScript 5).
Ready to dive in? Let's see some of the abilities in action!
Concepts
The most important concept in functional programming is that of pure functions. A pure function is idempotent and won't change any state. Mathematically this makes sense as functions such as sin(x)
seem to be quite natural and do not rely on any external state.
Besides having pure functions, we would also like to have a single argument functions. They are the most primitive. Zero argument functions usually indicate that an external state would be changed, thus not being pure. But in a language like JavaScript we will usually have functions taking more than a single argument.
Currying
The ability to have higher-order functions (i.e., functions that can take functions as input and emit a function as output) combined with closures (capturing local variables) gives us a nice way out: currying. Currying is a process where a function with multiple (let's say n
) arguments is transformed to a function with a single argument returning another function with a single argument. This goes on until all required arguments are collected.
Let's say we want to use the Ramda.js helper is
to write a one-argument wrapper which tests whether its argument is a string
. The following code will do the job.
function isString (test) {
return R.is(String, test);
}
var result = isString('foo'); //=> true
The same thing can be done much easier with currying. Since R.is
is part of Ramda.js, the library will automatically return a curried function if we supply less arguments that the function takes:
var isString = R.is(String);
var result = isString('foo'); //=> true
This is much more expressive. Since we used R.is
with a single argument we received a function. On the second call (remember, the original function call requires two arguments) we obtain the result.
But what if we did not start with a helper from Ramda.js in the first place? Let's pretend we already have the following function defined somewhere in our code:
var quadratic = (a, b, c, x) => x * x * a + x * b + c;
quadratic(1, 0, 0, 2); //=> 4
quadratic(1, 0, 0)(2); //=> TypeError: quadratic(..) is not a function
This is the full 2nd order polynomial. It has four parameters do allow all possible values. But usually, we will only want to change the x
for a fixed set of parameters a
, b
, and c
. Let's see how we can transform this with Ramda.js:
var quadratic = R.curry((a, b, c, x) => x * x * a + x * b + c);
quadratic(1, 0, 0, 2); //=> 4
quadratic(1, 0, 0)(2); //=> 4
Again, we are able to simple use the argument evaluation to alias specific subsets. For instance the equation x - 1
may be obtained by:
var xOffset = quadratic(0, 1, 1);
xOffset(0); //=> -1
xOffset(1); //=> 0
In cases where the number of arguments is not given by the parameters of our function we need to use curryN
and specify the number of arguments explicitly.
Currying is at the heart of Ramda.js, but without anything more the library would seem less interesting. Another concept that is important in functional programming is immutability.
Continue reading %Hands-on Functional Programming with Ramda.js%
by Florian Rappl via SitePoint