The VikiD Truth

Knowledge base / Computing

Read This! | Algorithms | AssemblyLanguage | BASH | C-Programmming | Computer Security | Databases | Emacs | Exploitation | Functional Programming | Firewalls | Git | Javascipt | Julia | Social Engineering | Picolisp | Pharo Smalltalk | Linux | Python | Operating Systems | Machine Learning | Metasploit | Open Data Science | Networking | Machine Learning |Scheme | Unity | vim | Web Development

Lambda Calculus

Here is a truth that still amazes me: you can build all of computation out of nothing but functions. No numbers, no booleans, no loops, no variables that change – just making functions and applying them. That tiny system is the lambda calculus, and every Lisp, every closure, every => in JavaScript descends from it.

What it is, in plain words

The lambda calculus, invented by Alonzo Church in the 1930s, has exactly three kinds of expression:

  1. Variables – x x , y y , f f .
  2. Function abstraction – λx. M \lambda x.\, M : “a function that takes x x and gives back M M ”.
  3. Function application – M N M\, N : “apply M M to N N ”.

And one real rule of computation, beta reduction: to apply a function, substitute the argument for the parameter.

(λx. M) N  →  M[x:=N] (\lambda x.\, M)\, N \;\to\; M[x := N]

For example (λx. x x) y→y y (\lambda x.\, x\, x)\, y \to y\, y .

That’s the whole language. Two helper rules keep it honest: alpha conversion (renaming a bound variable changes nothing: λx. x=λy. y \lambda x.\, x = \lambda y.\, y ) and, optionally, eta (λx. f x=f \lambda x.\, f\, x = f when x x isn’t free in f f ).

Why it matters

Church and Turing proved, independently and in the same year (1936), that the lambda calculus and the Turing machine can compute exactly the same things. So this tiny function language is a complete model of computation – the “assembly language” of functional programming.

It matters practically too:

An everyday analogy: fill-in-the-blank forms

A lambda is a form with a blank: “Dear ___, thank you for coming.” Abstraction is making the form and labelling the blank x x . Application is handing someone the form and a name. Beta reduction is filling in the blank wherever it appears. The surprising part is that the thing you write in the blank can be another form – and that’s enough to compute anything.

A hands-on example: numbers and booleans from nothing

With no built-in data, how do we get true and false? We define them by what they do: choose.

TRUE=λa. λb. aFALSE=λa. λb. b \text{TRUE} = \lambda a.\, \lambda b.\, a \qquad \text{FALSE} = \lambda a.\, \lambda b.\, b

Then IF=λc. λt. λe. c t e \text{IF} = \lambda c.\, \lambda t.\, \lambda e.\, c\, t\, e just applies the boolean to the two branches.

Numbers are “do something n n times” (Church numerals):

0=λf. λx. x1=λf. λx. f x2=λf. λx. f (f x) 0 = \lambda f.\, \lambda x.\, x \qquad 1 = \lambda f.\, \lambda x.\, f\, x \qquad 2 = \lambda f.\, \lambda x.\, f\,(f\, x)
SUCC=λn. λf. λx. f (n f x)PLUS=λm. λn. λf. λx. m f (n f x) \text{SUCC} = \lambda n.\, \lambda f.\, \lambda x.\, f\,(n\, f\, x) \qquad \text{PLUS} = \lambda m.\, \lambda n.\, \lambda f.\, \lambda x.\, m\, f\,(n\, f\, x)

You can run these directly in any language with first-class functions. In JavaScript:

const TRUE  = a => b => a;
const FALSE = a => b => b;
const IF    = c => t => e => c(t)(e);

const ZERO = f => x => x;
const SUCC = n => f => x => f(n(f)(x));
const PLUS = m => n => f => x => m(f)(n(f)(x));

const toInt = n => n(k => k + 1)(0);   // decode for humans
const TWO = SUCC(SUCC(ZERO));
const THREE = SUCC(TWO);

console.log(toInt(PLUS(TWO)(THREE)));      // 5
console.log(IF(TRUE)("yes")("no"));        // yes

And in PicoLisp, where a function is literally a list of parameters and body:

(setq Two '((F X) (F (F X))))
(Two inc 0)                       # -> 2
(Two '((S) (pack S "!")) "hi")    # -> "hi!!"

Recursion without names

There are no named definitions in the pure calculus, so how can a function call itself? With a fixed-point combinator:

Y=λf. (λx. f (x x)) (λx. f (x x)) Y = \lambda f.\, (\lambda x.\, f\,(x\, x))\,(\lambda x.\, f\,(x\, x))

Y g Y\, g reduces to g (Y g) g\,(Y\, g) – the function receives itself as an argument. In strict languages like JavaScript you use the variant called Z to avoid looping forever.

flowchart LR
  A["(λx. x x) y"] -- beta --> B["y y"]
  C["Y g"] -- beta --> D["g (Y g)"] -- beta --> E["g (g (Y g))"] --> F[...]

Common misconceptions

Connections

Last edited · 1 revisions

Tags: #Computing