Composable Functional JavaScript

Table of Contents

Recipes on Composing Functions in JavaScript

Composing Singular Pure Functions

const double = n => n * 2
const inc = n => n + 1
const square = n => n * n

const pipe = (...fns) => x => fns.reduce((y, f) => f(y), x)

const composed = pipe(inc, double, square)
console.log(composed(3)) // ((3 + 1) * 2 ) ^ 2 = 64;

Box

// Box is the identity functor!
const Box = x => ({
    map: f => Box(f(x)),
    fold: f => f(x),
    chain: f => f(x),
    ap: b2 => b2.map(x),
    toString: () => `Box(${x})`
})
Box.of = x => Box(x)

const result = Box('   42  ')
    .map(s => s.trim())
    .map(s => new Number(s))
    .fold(n => n + 1)
console.log(result.toString())

Lazy Box

LazyBox is used to delay evaluation. Nothing happens until fold is called.

const LazyBox = g => ({
    map: f => LazyBox(() => f(g())),
    fold: f => f(g()),
    toString: () => `LazyBox(${g})`
})

// example:
const result = LazyBox(() => '   42  ')
    .map(s => s.trim())
    .map(s => new Number(s))
    .map(n => n + 1)
    .fold(n => n) // fold makes things happen!
console.log(result.toString())

Maybe Monad

The Maybe monad is used to gracefully handle errors. The error state propagates throughout the expressions, eliminating the need to handle errors explicitly, such as with try/catch blocks.

const Left = x => ({
    map: f => Maybe(f(x)),
    fold: (f, g) => f(x), // exec the Left function
    toString: () => `Left(${x})`
})

const Right = x => ({
    map: f => Right(x), // f is ignored!
    fold: (f, g) => g(x), // exec the Right function
    toString: () => `Right(${x})`
})

const Maybe = x => (x != null ? Left(x) : Right(null))

An example:

const findColor = name =>
    Maybe(
        {
            red: '#ff4444',
            blue: '#3b5998',
            yellow: '#fff68f'
        }[name]
    )

const blueColorValue = findColor('blue')
    .map(c => c.slice(1))
    .map(c => c.toUpperCase())
    .fold(
        x => x,
        e => 'NOT FOUND'
    )

const bogusColorValue = findColor('bogus')
    .map(c => c.slice(1))
    .map(c => c.toUpperCase())
    .fold(
        x => x,
        e => 'NOT FOUND'
    )

console.log(
    `Example 3: blue has the value ${blueColorValue} and bogus has the value ${bogusColorValue}`
)

The Y Combinator

The Y combinator is an implementation of a fixed-point combinator in lambda calculus. In mathematics and computer science in general, a fixed point of a function is a value that is mapped to itself by the function. Essentially it is a way to implement recursion without self reference. In the lambda calculus it is not possible to refer to the definition of a function in a function body. Recursion may only be achieved by passing in a function as a parameter. The Y combinator demonstrates this style of programming. It does not have much practical value but is a bit fascinating to think about :-).

const Y = f => {
  const g = h => (...args) => f(h(h))(...args)
  return f(g(g))
}

// example:
const fac = Y(f => n => (n > 1 ? n \* f(n - 1) : 1))
const fib = Y(f => n => (n > 1 ? f(n - 1) + f(n - 2) : n))

console.log('fac(5):', fac(5))
console.log('fib(10):', fib(10))
Dr. Mike Hopper avatar
Dr. Mike Hopper
Software Architect, Engineer, and Instructor