functional-programming-jargon README

The project's README from github.com/hemanth/functional-programming-jargon. As one markdown file: README.md.

Functional Programming Jargon

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

Arity

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

Higher-Order Functions (HOF)

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]

Closure

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

Partial Application

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.

Currying

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

Auto Currying

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

Function Composition

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'

Continuation

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)
})

IO

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

Trampoline

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

Thunk

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

Pure Function

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.

Side effects

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!')

Idempotence

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])))

Point-Free Style

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.

Predicate

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]

Contracts

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

Category

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:

  1. There must be an identity morphism that maps an object to itself. Where a is an object in some category, there must be a function from a -> a.
  2. Morphisms must compose. Where 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).
  3. Composition must be associative 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

Value

Anything that can be assigned to a variable.

5
Object.freeze({ name: 'John', age: 30 }) // The `freeze` function enforces immutability.
;(a) => a
;[1]
undefined

Constant

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

Constant Function

A curried function that ignores its second argument:

const constant = a => () => a

;[1, 2].map(constant(0)) // => [0, 0]

Constant Functor

Object whose map doesn't transform the contents. See Functor.

Constant(1).map(n => n + 1) // => Constant(1)

Constant Monad

Object whose chain doesn't transform the contents. See Monad.

Constant(1).chain(n => Constant(n + 1)) // => Constant(1)

Functor

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)

Pointed Functor

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]

Lift

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]

Referential Transparency

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.

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...).

Memoization

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 

functional-programming-jargon (hemanth/functional-programming-jargon)

Searching Database…

Что это такое?Глоссарий, который простым языком объясняет терминологию функционального программирования (например, каррирование, монады, функторы) с короткими примерами на JavaScript.
Это библиотека или просто справочный документ?Это обычный справочный документ (глоссарий), а не код, который нужно устанавливать или импортировать; фрагменты на JavaScript являются лишь иллюстрациями.
Как устроен список и с чего мне начать?Он устроен как оглавление терминов (арность, замыкание, каррирование, функтор, монада и др.), у каждого есть короткое определение, пример кода и ссылки для дальнейшего чтения, так что можно сразу перейти к любому непонятному термину.
Нужно ли мне сначала знать JavaScript или какую-либо библиотеку для функционального программирования?Достаточно базового JavaScript (ES2015), так как все примеры написаны на нём; никакая библиотека для функционального программирования, например Ramda или Lodash, не нужна, хотя в некоторых записях они упоминаются.
Документ поддерживается в актуальном состоянии и переведён?Проект активно поддерживается, для него настроено автоматическое развёртывание, и он переведён на многие языки, включая португальский, испанский, китайский, корейский, польский, русский и другие.
Можно ли просматривать это как интерактивный граф, а не прокручивать README?Да, есть интерактивная визуализация графа на hemanth.github.io/functional-programming-jargon, которая связывает между собой близкие термины.
Есть ли версия для ИИ-агентов или LLM?Да, проект предоставляет отдельный файл спецификации llms.txt для использования агентами и LLM: hemanth.github.io/functional-programming-jargon/llms.txt.
Это только определения или здесь есть и настоящий код?Каждый термин включает краткое определение и запускаемые фрагменты кода на JavaScript, которые демонстрируют понятие, а также ссылки для дальнейшего чтения, например на книгу 'Category Theory for Programmers'.
Creator
hemanth
Size
1.2 MB
Stars
18.7K
Updated
CommitOct 04
Age
11y
Commits · 90 Days