Functional programming with Ramda.js

05 September, 2015

JavaScript is one of the most dynamic, flexible and powerful programming languages. It supports multiple programming paradigms - imperative, object (prototype) oriented, scripting, and functional.
Let’ see what JavaScript has common with functional programming languages:

By the way, there are some significant differences:

JavaScript ecosystem has great tools for advanced use of functional programming features, such as Underscore and Lodash - most popular toolkit libraries. But if you want use library, that was specifically designed for functional programming, you may have to look at Ramda.js .

Ramda.js has some distinguishing features:

To show examples of Ramda.js using, i will use io.js 3.2 and Babel, so let’s create new .js file:

#!/usr/bin/env babel-node

import R from 'ramda';

Ramda.js API has some general use functions:

// Typing
let str = 'test';
R.is(String, str); //=> true
// The same with currying
R.is(String)(str); //=> true
let isString = R.is(String);
isString('string'); //=> true
R.type(isString); //=> String

// Math
R.add(100, 500); //=> 600
R.add(100)(500); //=> 600
R.mean([2,3,7]); //=> 4
R.sum(R.range(1, 5)); //=> 10

// Logic
R.and(true, false); //=> false
R.and([])(0); //=> 0
R.not(1); //=> false
R.both(isString, R.is(String))(str); // => true

Like the Underscore and Lodash, Ramda has collection helper functions:

// Lists
let animals = [
  {
    name: 'goose',
    type: 'bird',
    color: 'white'
  },
  {
    name: 'parrot',
    type: 'bird',
    color: 'yellow'
  },
  {
    name: 'cat',
    type: 'mammal',
    color: 'grey'
  }
];

R.map(animal => animal.color + ' ' + animal.name, animals); //=> [ 'white goose', 'yellow parrot', 'grey cat' ]
R.head(animals).name; //=> goose
R.last(animals).name; //=> cat
R.uniq(R.pluck('type', animals)); //=> [ 'bird', 'mammal' ]
R.length(R.filter(animal => animal.type === 'bird', animals)); //=> 2

And object helpers too:

// Objects
let cat = {
  type: 'animal',
  subclass: 'mammal',
  binomialName: 'Felis catus'
};
R.assoc('status', 'domesticated', cat).status; //=> domesticated
R.dissoc('binomialName', cat).binomialName; //=> undefined
R.keys(cat); //=> [ 'type', 'subclass', 'binomialName' ]
R.has('type', cat); //=> true
R.prop('type', cat); //=> animal
R.values(cat); //=> [ 'animal', 'mammal', 'felis catus' ]

// Object transformation
let transformations = {
  type: R.toUpper,
  binomialName: R.toLower
}
R.evolve(transformations, cat).type; // => ANIMAL

But key point of Ramda.js is functions. Ramda allows you to easily compose multiple functions in different orders:

// Compose and pipe
R.join(' and ', R.uniq(R.map(R.toUpper)(R.pluck('type', animals)))); //=> BIRD and MAMMAL
// Performs right-to-left function composition
R.compose(
  R.join(' and '),
  R.uniq,
  R.map(R.toUpper),
  R.pluck('type')
)(animals); //=> BIRD and MAMMAL
// Performs left-to-right function composition
R.pipe(
  R.pluck('type')
  R.map(R.toUpper),
  R.uniq,
  R.join(' and ')
)(animals); //=> BIRD and MAMMAL

Another power of Ramda is currying. Currying is the process of translating evaluation of function that takes multiple parameters in evaluating a sequence of functions, each with one argument.

let tripleMultiply = (a, b, c) => a * b * c;
tripleMultiply(3, 9, 2); //=> 54
tripleMultiply(3, 9)(2); //=> TypeError: tripleMultiply(..) is not a function
let curriedMultiply = R.curry(tripleMultiply);
curriedMultiply(3, 9)(2); //=> 54
curriedMultiply(3)(9)(2); //=> 54

Pattern matching is also available through R.cond. That allows you to check sequence of conditions to match different patterns:

let checkNumber = R.cond([
  [R.is(Number), R.identity],
  [R.is(String), parseInt],
  [R.is(Boolean), Number],
  [R.isEmpty, R.always(0)],
  [R.T, R.always(NaN)]
]);
checkNumber(100500); //=> 100500
checkNumber('146%'); //=> 146
checkNumber('Hodor'); //=> NaN
checkNumber(true); //=> 1
checkNumber(false); //=> 0
checkNumber([]); //=> 0
checkNumber(['test']); //=> NaN

Ramda.js is one of the best functional programming libraries that exists in JavaScript ecosystem. It can completely replace Underscore, Lodash in your project with own object, lists and others helpers. Immutability, currying and composing allows you to write both efficient and simple code in pure functional style.