Glossary

The FP paradigm has a lot of terms which get thrown about which might need clarification, so use this listing. Additionally, you can link to any valid defintion with /glossary/<term>, e.g. open.sorcerers.dev/glossary/function

A relationship or expression involving one or more variables.

const a = () => `hip arrow function`
const b = function () { return `assigned anonymous function` }
function c() { return `named function` }

The number of parameters a function takes.

// each of these is a number in ordinal form
const nullary = () => {}
const unary = x => x
const binary = (a, b) => [a, b]
const ternary = (a, b, c) => ({a, b, c})
const quaternary = (a, b, c, d) => "the name of 5 parameters is toomanyary"

A function which takes zero parameters, or is said to have an arity of 0.

const nullary = () => `nothing`

A function which takes one parameter, or is said to have an arity of 1.

const unary = thing => `one ${thing}`

A function which takes two parameters, or is said to have an arity of 2.

const binary = (thing1, thing2) => `${thing1} => ${thing2}`
const binaryMC = thing1 => thing2 => `${thing1} => ${thing2}`
const binaryC = curry((thing1, thing2) => `${thing1} => ${thing2}`)

A function which takes three parameters, or is said to have an arity of 3.

const ternary = (a, b, c) => a + b + c
const ternaryMC = a => b => c => a + b + c 
const ternaryC = curry((a, b, c) => a + b + c)

A pure function is deterministic, has no side-effects and is referentially transparent.

const add = curry((a, b) => a + b)
const addTwo = add(2)
addTwo(100) / add(200)(111)
(2+100) / (200+111)
102 / 311

A function which will always return the same output given the same input. As a feature of this, these functions are referentially transparent:

import {curry} from 'ramda'

const times = curry((a, b) => a * b)
const twice = times(2)
const doubleAll = map(twice)
doubleAll([123,345,567]) === [246, 690, 1134]
// you can then work backwards from this:
map(twice)([123,345,567]) === [246, 690, 1134]
map(times(2))([123,345,567]) === [246, 690, 1134]
[
  times(2)(123),
  times(2)(345),
  times(2)(567)
] === [246, 690, 1134]

That was a simple example but this maxim holds true no matter how complex the nested complexity gets. See this review for more examples.

A function which (when invoked with all inputs) can be replaced in all instances without changing the program's behavior.

const times = a => b => a * b
const double = times(2)
const ten = double(5) 
const four = double(2) 
const forty = times(ten)(four)
times(ten)(four) === times(times(2)(5))(times(2)(2))
// === times(2 * 5)(2 * 2)
// === 2 * 5 * 2 * 2

A side-effect is something which is external to the body of the current function (often in the form of a function call whose value is not captured).

const whatever = () => {
  console.log('a log is a side-effect')
  return Math.random() * 200
}

A free-variable is a global variable or variable defined externally to the function.

// window is a free variable with respect to `save`
const save = data => window.localStorage.setItem('saved', data)

You can use the free-variable partial-application pattern to fix this problem and turn this function pure:

const checkWindowExists = () => typeof window !== 'undefined'

const saveWithContext = fn => data => fn('saved', data)
// [...]

if (checkWindowExists()) {
  const save = saveWithContext(window.localStorage.setItem.bind(window.localStorage))
}

If the above seems overkill, or needless, just remember that a runtime error can crash everything in JS land and a free benefit of the pattern is that you get the ability to inject mocks into your tests (e.g. const save = saveWithContext(jest.fn()) for instance). So pick your battles.

A function which takes a function and possible additional parameters, and returns a function.

const map = fn => xs => xs.map(x => fn(x))
// import {map} from 'ramda'
map(times(2))([1,2,3]) === [2,4,6]

Currying is a way of modifying a given function which takes multiple arguments in to a sequence of unary functions.

Specifically, in JS, it means that you can manipulate arguments, their order, and other facets of a passed in function.

const manual = a => b => c => a + b / c
import {curry} from 'ramda'
const auto = curry((a, b, c) => a + b / c)
manual(1)(9)(2) === auto(1)(9)(2)
manual(1)(9)(2) === auto(1, 9)(2)
manual(1)(9)(2) === auto(1, 9, 2)
manual(1)(9)(2) === auto(1)(9, 2)

Modules

ramdalodashkatsu-curryfpo

An operation which takes two or more functions and returns a unary function.

Function composition facilitates code re-use.

const f = x =>  x
const g = x => y
const h = x => f(g(x))
// or, es6 style:
import { pipe } from 'ramda'
const h = pipe(f, g)
// if you need to roll your own
const pipe = (...fns) => (
  x => fns.reduce(
    (prev, fn) => fn(prev),
    x
  )
)

A combinator is a primitive higher-order function without free-variables.

// identity combinator
const I = x => x
// constant combinator
const K = x => () => x
// apply combinator
const A = f => x => f(x)
// thrush combinator
const T = x => f => f(x) 
// duplication combinator
const W = f => x => f(x)(x)
// flip combinator
const C = f => y => x => f(x)(y) 
// compose combinator
const B = f => g => x => f(g(x))
// amalgamation combinator
const S = f => g => x => f(x)(g(x))
// psi combinator
const P = f => g => x => y => f(g(x))(g(y))
// fixed-point Y combinator
const Y = f => (g => g(g))(g => f(x => g(g)(x))) 

// morphisms
S(K)(K) === I
C(A) === T
W(T)(I) === I
C(W(K)(T)) === A
S(K)(A) === A

Often if you can identify combinators in your code, you can use them to simplify logical parts of the code because of a knowledge of certain morphisms.

Read much more on wikipedia. (The above definitions provided by this gist.)

(A common reference is To Mock A Mockingbird but your mileage may vary.)

There's a lot more to read about but one of the reasons it's cool is that using just S and K above, you can define a Turing complete language.

The constant function takes what it is given and returns a nullary function which returns the original value. Sometimes called always or the K combinator.

const K = x => () => x
// import { always as K } from 'ramda'
const alwaysCool = K('cool')
alwaysCool() === 'cool'

The identity function returns what it is given. Sometimes referenced as the I combinator.

const I = x => x
// import { identity as I } from 'ramda'
'so\n\n\n\n\nso\n\n\ncool'.split('\n').filter(I)
See this page on Github
Built with 💛 by Open Sorcerers
Created with Gatsby using the 😎 foresight starter