Functional Programming with Ramda.js

React.js library with a focused FP goal

/logos/2019.09.13-ramda.jpg

Ramda.js

Ramda is a library designed specifically for a functional programming style. It makes it easy to create functional programming pipelines, and never mutates user data. JavaScript’s Array.prototype class has some functionally-flavored methods such as map, reduce, and filter. However, these only operate on arrays. Ramda is described as more generic, being that it can work with strings, objects, and user-defined data types. There are other convenience libraries that allow functional operations on objects, such as lodash, but Ramda is much more functional by design; explicitly eliminating the possibility side-effects, and facilitating a divergence from the very imperative style of JavaScript to a more declarative model of programming.

Composition with Functions

So why would I want to use Ramda when lodash is easier to understand? Ramda allows functions to be used as first-order components.

Ramda REPL

The ramda REPL is a great way to test Ramda code in a live environment.

Functional Programming

Pure functions - pure functions have no side-effects. They don’t consume input, produce output, or assign to any outside variables. The exclusive raison d’ĂȘtre of a pure function is to deterministically return some value derived from the parameters passed into it.

Immutability - immutability is the concept that data is not mutated. Since pure functions can’t change outside data structures, immutability is necessarily essential to working with pure functions. Once a value or object is initialized, it is never changed again; instead, pure functions are used to parse and interpret that value.

Functions are first-class constructs in Ramda - meaning they can be passed as parameters to other functions, or returned as output.

Higher Order Functions - Higher Order Functions are premised on lower order functions - they either take or return other functions as part of their composition.

Terminology

Currying - Currying is the process of turning a function that expects multiple parameters into one that, when supplied fewer parameters, returns a new function that awaits the remaining ones. Functor - A functor is a map between categories.

Collection-iteration functions (Pipelines)

JavaScript’s Array.prototype class is most familiar to try functional pipelines. It has a number of functionally-flavored methods like (map, reduce, and filter, find, forEach, includes, some, every) These resemble functional programming constructs, but are potentially capable of mutating data and producing side-effects.

Higher Order Functions

Higher order functions pass functions to other functions. Combination of these functions allows for unique capabilities. Ramda provides a higher-order function, complement, that takes another function and returns a new function that returns true when the original function returns a falsy value, and false when the original function returns a truthy value. Since even and odd numbers are compliments to each other, this can be demonstrated.

const isEven = x => x % 2 === 0
const isOdd = complement(isEven)

find(isOdd, [1, 2, 3, 4]) // --> 1

Pipe function

The pipe function takes a list of one or more functions, and returns a new function. That function requires the arguments that would be passed into the first function in the pipeline, the output of which is passed into the subsequent function. All the subsequent functions can only accept a single argument as input.

const multiply = (a, b) => a * b
const addOne = x => x + 1
const square = x => x * x

const pipeline = (x, y) => {
  const product = multiply(x, y)
  const incremented = addOne(product)
  const squared = square(incremented)

  return squared
}

expressed as a pipe:

const pipeline = pipe(
  multiply,
  addOne,
  square
)

Compose function

Pipelines evaluate functions in a left-to-right sequence. The compose function operates in a right-to-left manner, which is similar to how inlined functions would appear.

const pipeline = pipe(
  multiply,
  addOne,
  square
)

As an inlined pipe:

const pipeline = (x, y) => square(addOne(multiply(x, y)))

Another way to think of compose is that compose(foo, bar)(input) is equivalent to foo(bar(input))

Collection Iteration to Functional Composition

For this example, we take a collection of books with titles and the year of their publishing. The goal is to use Ramda to combine the map and filter functions, although we first need to understand how work with multiple functions that require more than one input argument. We start out just using essentially the same collection iteration functions that are familiar from vanilla JS:

/Screenshot_2019-09-16_16-45-31-ramda-1.png

Another way that we could consider doing this is changing the publishedInYear() function for an inline ES6 function.

/Screenshot_2019-09-16_17-23-04-ramda-2.png

We can revert to a named publishedInYear(), although I think the composition concept is easier to understand in light of that intermediate step.

/Screenshot_2019-09-16_17-28-09-ramda-3.png

Hopefully this starts to bring some clarity to the idea of using functions as first order components. Essentially, publishedInYear() now returns a function that takes book as a parameter.

Partially-Applying Functions

The Ramda functions partial and partialRight further help us to work with pipelines that call a function with fewer arguments than it requires.