The project's README from github.com/hemanth/functional-programming-jargon. As one markdown file: README.md.
Functional programming (FP) provides many advantages, and its popularity has been increasing as a result. However, each programming paradigm comes with its own unique jargon and FP is no exception. By providing a glossary, we hope to make learning FP easier.
Examples are presented in JavaScript (ES2015). Why JavaScript?
Where applicable, this document uses terms defined in the Fantasy Land spec.
🌐 Interactive Graph: hemanth.github.io/functional-programming-jargon 🤖 Agent / LLM Spec: hemanth.github.io/functional-programming-jargon/llms.txt
Translations
Table of Contents
The number of arguments a function takes. From words like unary, binary, ternary, etc.
const sum = (a, b) => a + b
// The arity of sum is 2 (binary)
const inc = a => a + 1
// The arity of inc is 1 (unary)
const zero = () => 0
// The arity of zero is 0 (nullary)
Further reading
A function which takes a function as an argument and/or returns a function.
const filter = (predicate, xs) => xs.filter(predicate)
const is = (type) => (x) => Object(x) instanceof type
filter(is(Number), [0, '1', 2, null]) // [0, 2]
A closure is a scope which captures local variables of a function for access even after the execution has moved out of the block in which it is defined. This allows the values in the closure to be accessed by returned functions.
const addTo = x => y => x + y
const addToFive = addTo(5)
addToFive(3) // => 8
In this case the x is retained in addToFive's closure with the value 5. addToFive can then be called with the y
to get back the sum.
Further reading/Sources
Partially applying a function means creating a new function by pre-filling some of the arguments to the original function.
// Helper to create partially applied functions
// Takes a function and some arguments
const partial = (f, ...args) =>
// returns a function that takes the rest of the arguments
(...moreArgs) =>
// and calls the original function with all of them
f(...args, ...moreArgs)
// Something to apply
const add3 = (a, b, c) => a + b + c
// Partially applying `2` and `3` to `add3` gives you a one-argument function
const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c
fivePlus(4) // 9
You can also use Function.prototype.bind to partially apply a function in JS:
const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c
Partial application helps create simpler functions from more complex ones by baking in data when you have it. Curried functions are automatically partially applied.
The process of converting a function that takes multiple arguments into a function that takes them one at a time.
Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.
const sum = (a, b) => a + b
const curriedSum = (a) => (b) => a + b
curriedSum(40)(2) // 42.
const add2 = curriedSum(2) // (b) => 2 + b
add2(10) // 12
Transforming a function that takes multiple arguments into one that if given less than its correct number of arguments returns a function that takes the rest. When the function gets the correct number of arguments it is then evaluated.
Lodash & Ramda have a curry function that works this way.
const add = (x, y) => x + y
const curriedAdd = _.curry(add)
curriedAdd(1, 2) // 3
curriedAdd(1) // (y) => 1 + y
curriedAdd(1)(2) // 3
Further reading
The act of putting two functions together to form a third function where the output of one function is the input of the other. This is one of the most important ideas of functional programming.
const compose = (f, g) => (a) => f(g(a)) // Definition
const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage
floorAndToString(121.212121) // '121'
At any given point in a program, the part of the code that's yet to be executed is known as a continuation.
const printAsString = (num) => console.log(`Given ${num}`)
const addOneAndContinue = (num, cc) => {
const result = num + 1
cc(result)
}
addOneAndContinue(2, printAsString) // 'Given 3'
Continuations are often seen in asynchronous programming when the program needs to wait to receive data before it can continue. The response is often passed off to the rest of the program, which is the continuation, once it's been received.
const continueProgramWith = (data) => {
// Continues program with data
}
readFileAsync('path/to/file', (err, response) => {
if (err) {
// handle error
return
}
continueProgramWith(response)
})
A pure data structure that encapsulates a side effect. Instead of performing the effect immediately, IO wraps the action in a nullary function (thunk), allowing effectful operations to be transformed, chained, and composed as pure values without actually executing them until explicitly triggered.
const IO = (run) => ({
run,
map: (f) => IO(() => f(run())),
chain: (f) => IO(() => f(run()).run())
})
// Pure description - nothing executes yet
const readTimestamp = IO(() => Date.now())
const formatted = readTimestamp.map((ts) => new Date(ts).toISOString())
// Side effect executes only when calling .run()
formatted.run()
Further reading
A mechanism that enables deep or mutually recursive functions to run without exceeding the maximum call stack limit.
In environments without Tail Call Optimization (TCO), recursive calls return a function (a thunk) instead of invoking themselves directly. The trampoline runs a while-loop that unwinds each thunk until a final value is reached.
const trampoline = (fn) => (...args) => {
let result = fn(...args)
while (typeof result === 'function') {
result = result()
}
return result
}
// Without trampoline: sumBelow(1000000) throws "Maximum call stack size exceeded"
const sumBelow = (n, acc = 0) =>
n === 0
? acc
: () => sumBelow(n - 1, acc + n) // returns a thunk instead of recursing directly
const safeSum = trampoline(sumBelow)
safeSum(1000000) // 500000500000
Further reading
A nullary function (a function taking zero arguments) that wraps an expression to delay its evaluation until called. Thunks are the fundamental mechanism for implementing lazy evaluation, trampolines, and deferred side effects.
// An eager calculation executes immediately:
// const data = expensiveCalculation()
// A thunk wraps the expression in a function, deferring execution:
const thunk = () => 42 * 2
// The expression is only evaluated when explicitly called:
thunk() // 84
Further reading
A function is pure if the return value is only determined by its input values, and does not produce side effects. The function must always return the same result when given the same input.
const greet = (name) => `Hi, ${name}`
greet('Brianne') // 'Hi, Brianne'
As opposed to each of the following:
window.name = 'Brianne'
const greet = () => `Hi, ${window.name}`
greet() // "Hi, Brianne"
The above example's output is based on data stored outside of the function...
let greeting
const greet = (name) => {
greeting = `Hi, ${name}`
}
greet('Brianne')
greeting // "Hi, Brianne"
... and this one modifies state outside of the function.
A function or expression is said to have a side effect if apart from returning a value, it interacts with (reads from or writes to) external mutable state.
const differentEveryTime = new Date()
console.log('IO is a side effect!')
A function is idempotent if reapplying it to its result does not produce a different result.
Math.abs(Math.abs(10))
sort(sort(sort([2, 1])))
Writing functions where the definition does not explicitly identify the arguments used. This style usually requires currying or other Higher-Order functions. A.K.A Tacit programming.
// Given
const map = (fn) => (list) => list.map(fn)
const add = (a) => (b) => a + b
// Then
// Not point-free - `numbers` is an explicit argument
const incrementAll = (numbers) => map(add(1))(numbers)
// Point-free - The list is an implicit argument
const incrementAll2 = map(add(1))
Point-free function definitions look just like normal assignments without function or =>. It's worth mentioning that point-free functions are not necessarily better than their counterparts, as they can be more difficult to understand when complex.
A predicate is a function that returns true or false for a given value. A common use of a predicate is as the callback for array filter.
const predicate = (a) => a > 2
;[1, 2, 3, 4].filter(predicate) // [3, 4]
A contract specifies the obligations and guarantees of the behavior from a function or expression at runtime. This acts as a set of rules that are expected from the input and output of a function or expression, and errors are generally reported whenever a contract is violated.
// Define our contract : int -> boolean
const contract = (input) => {
if (typeof input === 'number') return true
throw new Error('Contract violated: expected int -> boolean')
}
const addOne = (num) => contract(num) && num + 1
addOne(2) // 3
addOne('some string') // Contract violated: expected int -> boolean
A category in category theory is a collection of objects and morphisms between them. In programming, typically types act as the objects and functions as morphisms.
To be a valid category, three rules must be met:
a is an object in some category,
there must be a function from a -> a.a, b, and c are objects in some category,
and f is a morphism from a -> b, and g is a morphism from b -> c;
g(f(x)) must be equivalent to (g • f)(x).f • (g • h) is the same as (f • g) • h.Since these rules govern composition at very abstract level, category theory is great at uncovering new ways of composing things.
As an example we can define a category Max as a class:
class Max {
constructor (a) {
this.a = a
}
id () {
return this
}
compose (b) {
return this.a > b.a ? this : b
}
toString () {
return `Max(${this.a})`
}
}
new Max(2).compose(new Max(3)).compose(new Max(5)).id().id() // => Max(5)
Further reading
Anything that can be assigned to a variable.
5
Object.freeze({ name: 'John', age: 30 }) // The `freeze` function enforces immutability.
;(a) => a
;[1]
undefined
A variable that cannot be reassigned once defined.
const five = 5
const john = Object.freeze({ name: 'John', age: 30 })
Constants are referentially transparent. That is, they can be replaced with the values that they represent without affecting the result.
With the above two constants the following expression will always return true.
john.age + five === ({ name: 'John', age: 30 }).age + 5
A curried function that ignores its second argument:
const constant = a => () => a
;[1, 2].map(constant(0)) // => [0, 0]
Object whose map doesn't transform the contents. See Functor.
Constant(1).map(n => n + 1) // => Constant(1)
Object whose chain doesn't transform the contents. See Monad.
Constant(1).chain(n => Constant(n + 1)) // => Constant(1)
An object that implements a map function that takes a function which is run on the contents of that object. A functor must adhere to two rules:
Preserves identity
object.map(x => x)
is equivalent to just object.
Composable
object.map(x => g(f(x)))
is equivalent to
object.map(f).map(g)
(f, g are arbitrary composable functions)
The reference implementation of Option is a functor as it satisfies the rules:
Some(1).map(x => x) // = Some(1)
and
const f = x => x + 1
const g = x => x * 2
Some(1).map(x => g(f(x))) // = Some(4)
Some(1).map(f).map(g) // = Some(4)
An object with an of function that puts any single value into it.
ES2015 adds Array.of making arrays a pointed functor.
Array.of(1) // [1]
Lifting is when you take a value and put it into an object like a functor. If you lift a function into an Applicative Functor then you can make it work on values that are also in that functor.
Some implementations have a function called lift, or liftA2 to make it easier to run functions on functors.
const liftA2 = (f) => (a, b) => a.map(f).ap(b) // note it's `ap` and not `map`.
const mult = a => b => a * b
const liftedMult = liftA2(mult) // this function now works on functors like array
liftedMult([1, 2], [3]) // [3, 6]
liftA2(a => b => a + b)([1, 2], [30, 40]) // [31, 41, 32, 42]
Lifting a one-argument function and applying it does the same thing as map.
const increment = (x) => x + 1
lift(increment)([2]) // [3]
;[2].map(increment) // [3]
Lifting simple values can be simply creating the object.
Array.of(1) // => [1]
An expression that can be replaced with its value without changing the behavior of the program is said to be referentially transparent.
Given the function greet:
const greet = () => 'Hello World!'
Any invocation of greet() can be replaced with Hello World! hence greet is
referentially transparent. This would be broken if greet depended on external
state like configuration or a database call. See also Pure Function and
Equational Reasoning.
When an application is composed of expressions and devoid of side effects, truths about the system can be derived from the parts. You can also be confident about details of your system without having to go through every function.
const grainToDogs = compose(chickenIntoDogs, grainIntoChicken)
const grainToCats = compose(dogsIntoCats, grainToDogs)
In the example above, if you know that chickenIntoDogs and grainIntoChicken
are pure then you know that the composition is pure. This can be taken further
when more is known about the functions (associative, commutative, idempotent, etc...).
An optimization technique that caches the return value of a function based on its input parameters. Memoization is only valid and safe for pure functions possessing referential transparency, because calling the function with identical arguments must always yield identical results without producing observable side effects.
const memoize = (fn) => {
const cache = new Map()
return (arg) => {
if (!cache.has(arg)) {
cache.set(arg, fn(arg))
}
return cache.get(arg)
}
}
const