User:Thepigdog/Lambda Calculus - Church encoding

The natural step is to add primitive data types to Lambda Calculus to turn it into a practical language. Alonzo Church showed that, at least theoretically, this is not necessary. He showed that Lambda functions in normal form may be considered as representations of primitive data types, using the Church encoding. This is the basis for the Church-Turing thesis, which is that Lambda Calculus is Turing complete, without the addition of primitive data types. Turing complete means that any effectively computable operator may be implemented in Lambda Calculus.

The Church encoding includes representations for natural numbers, Booleans, pairs, lists, and tagged unions. Representations of data values are provided along with functions to manipulate the data values.

The Church encoding is a representation of data values. However the Lambda Calculus does not specific binary data to be created or used. If specific binary data was required another program would be needed to map between the church encoding and the native data types.

The use of these representations of native types is not intended as a practical measure. There are two problems,
 * There is no type checking.
 * The implementation is inefficient.

However the way native data types can be modeled as functions is of interest for providing an understanding of Lambda Calculus.

Natural numbers
Church numerals are the representations of natural numbers under Church encoding. The higher-order function that represents natural number $$n$$ is a function that maps any function $$f$$ to its n-fold composition. In simpler terms, the "value" of the numeral is equivalent to the number of times the function encapsulates its argument.


 * $$f^n = \underbrace{f \circ f \circ \cdots \circ f}_{n\text{ times}}.\,$$

Then starting with zero, being not applying the function, then one being applying the function, ...

Predecessor
To build a predecessor function we need a way of applying the function 1 less time. A numeral n applies the function f n times to x. The predecessor function must use the numeral n to apply the function n-1 times.

Before implementing the predecessor function, here is a scheme that wraps the value in a container function. We will define new function to use in place of f and x, called inc and init. The container function is called value. The left hand side of the table shows a numeral n applied to inc and init.

The general recurrence rule is,
 * $$ \operatorname{inc}\ (\operatorname{value}\ v) = \operatorname{value}\ (f\ v)$$

If there is also a function to retrieve the value from the container (called extract),
 * \operatorname{extract}\ (\operatorname{value}\ v) = v

Then extract may be used to define the same function as,
 * $$\operatorname{same} = \lambda n.\lambda f.\lambda x.\operatorname{extract}\ (n \operatorname{inc} \operatorname{init}) = \lambda n.\lambda f.\lambda x.\operatorname{extract}\ (\operatorname{value}\ (n\ f\ x)) = \lambda n.\lambda f.\lambda x.n\ f\ x = \lambda n.n$$

The same function is not intrinsically useful. What we want is the initial value to skip an application of f. Call this new initial container const. The right hand side of the above table shows the expansions of n inc const. Then by replacing init with const in the expression for the same function we get the predecessor function,


 * $$\operatorname{pred} = \lambda n.\lambda f.\lambda x.\operatorname{extract}\ (n \operatorname{inc} \operatorname{const}) = \lambda n.\lambda f.\lambda x.\operatorname{extract}\ (\operatorname{value}\ ((n-1)\ f\ x)) = \lambda n.\lambda f.\lambda x.(n-1)\ f\ x = \lambda n.(n-1)$$

As explained below the functions inc, init, const, value and extract may be defined as,

Which gives the lambda expression for pred as,
 * $$\operatorname{pred} = \lambda n.\lambda f.\lambda x.n\ (\lambda g.\lambda h.h\ (g\ f))\ (\lambda u.x)\ (\lambda u.u) $$

Note that there are no negative numbers, and,
 * $$\operatorname{pred} 0 = 0 $$

Subtraction
Then subtraction is defined using pred.

Note that minus returns zero if m > n.

Boolean logic
Boolean logic may be considered as a choice. Given two parameters and two values, a true function could choose the first parameter, and the false. The two definitions are known as Church Booleans.
 * $$\operatorname{true} = \lambda a.\lambda b.a$$
 * $$\operatorname{false} = \lambda a.\lambda b.b$$

Note that $$\operatorname{false}$$ is the same as as the numeral zero.

Because true and false choose the first or second parameter they may be combined to provide logic operators,
 * $$\operatorname{and} = \lambda p.\lambda q.p\ q\ p$$
 * $$\operatorname{or} = \lambda p.\lambda q.p\ p\ q$$
 * $$\operatorname{not1} = \lambda p.\lambda a.\lambda b.p\ b\ a$$ - This is only a correct implementation if the evaluation strategy is applicative order.
 * $$\operatorname{not2} = \lambda p.\lambda a.\lambda b.p\ (\lambda a.\lambda b. b)\ (\lambda a.\lambda b. a)$$ - This is only a correct implementation if the evaluation strategy is normal order.
 * $$\operatorname{xor} = \lambda a.\lambda b.a\ (\operatorname{not}\ b)\ b$$
 * $$\operatorname{IfThenElse} = \lambda p.\lambda a.\lambda b.p\ a\ b$$

For example:
 * $$\operatorname{and} \operatorname{true} \operatorname{false} = (\lambda p.\lambda q.p\ q\ p)\ \operatorname{true}\ \operatorname{false} = \operatorname{true} \operatorname{false} \operatorname{true} = (\lambda a.\lambda b.a) \operatorname{false} \operatorname{true} = \operatorname{false} $$


 * $$\operatorname{or} \operatorname{true} \operatorname{false} = (\lambda p.\lambda q.p\ p\ q)\ (\lambda a.\lambda b.a)\ (\lambda a.\lambda b.b) = (\lambda a.\lambda b.a)\ (\lambda a.\lambda b.a)\ (\lambda a.\lambda b.b) = (\lambda a.\lambda b.a) = \operatorname{true} $$


 * $$\operatorname{not1}\ \operatorname{true} = (\lambda p.\lambda a.\lambda b.p\ b\ a) (\lambda a.\lambda b.a) = \lambda a.\lambda b.(\lambda a.\lambda b.a)\ b\ a = \lambda a.\lambda b.(\lambda x.b)\ a = \lambda a.\lambda b.b = \operatorname{false} $$


 * $$\operatorname{not2}\ \operatorname{true} = (\lambda p.p\ (\lambda a.\lambda b. b) (\lambda a.\lambda b. a)) (\lambda a.\lambda b. a) = (\lambda a.\lambda b. a) (\lambda a.\lambda b. b) (\lambda a.\lambda b. a) = (\lambda b. (\lambda a.\lambda b. b))\ (\lambda a.\lambda b. a) = \lambda a.\lambda b.b = \operatorname{false} $$

Predicates
A predicate is a function that returns a Boolean value. The most fundamental predicate is $$\operatorname{IsZero}$$, which returns $$\operatorname{true}$$ if its argument is the Church numeral $$0$$, and $$\operatorname{false}$$ if its argument is any other Church numeral:
 * $$\operatorname{IsZero} = \lambda n.n\ (\lambda x.\operatorname{false})\ \operatorname{true}$$

The following predicate tests whether the first argument is less-than-or-equal-to the second:
 * $$\operatorname{LEQ} = \lambda m.\lambda n.\operatorname{IsZero}\ (\operatorname{minus}\ m\ n)$$,

Because of the identity,
 * $$ x = y \equiv (x <= y \land y <= x) $$

The test for equality may be implemented as,
 * $$\operatorname{EQ} = \lambda m.\lambda n.\operatorname{and}\ (\operatorname{LEQ}\ m\ n)\ (\operatorname{LEQ}\ n\ m) $$

Tuples with two values
Church pairs are an encoding of a tuple with a pair of values. The pair is represented as a function that takes a function argument. When given its argument it will apply the argument to the two components of the pair.


 * $$\operatorname{pair} = \lambda x.\lambda y.\lambda z.z\ x\ y $$
 * $$\operatorname{first} = \lambda p.p\ (\lambda x.\lambda y.x) $$
 * $$\operatorname{second} = \lambda p.p\ (\lambda x.\lambda y.y) $$

For example,
 * $$ \operatorname{first}\ (\operatorname{pair}\ a\ b) $$
 * $$ = (\lambda p.p\ (\lambda x.\lambda y.x))\ ((\lambda x.\lambda y.\lambda z.z\ x\ y)\ a\ b) $$
 * $$ = (\lambda p.p\ (\lambda x.\lambda y.x))\ (\lambda z.z a b) $$
 * $$ = (\lambda z.z\ a\ b)\ (\lambda x.\lambda y.x) $$
 * $$ = (\lambda x.\lambda y.x)\ a\ b = a $$

List
A list can be encoded by identifying it with its right fold function. For example, a list of three elements x, y and z can be encoded by a higher-order function which when applied to a combinator c and a value n returns c x (c y (c z n))).


 * $$\operatorname{nil} = \lambda c.\lambda n.n $$
 * $$\operatorname{isnil} = \lambda l.l\ (\lambda h.\lambda t.\operatorname{false})\ \operatorname{true} $$
 * $$\operatorname{cons} = \lambda h.\lambda t.\lambda c.\lambda n.c\ h\ (t\ c\ n) $$
 * $$\operatorname{head} = \lambda l.l\ (\lambda h.\lambda t.h)\ \operatorname{false} $$
 * $$\operatorname{tail} = \lambda l.\operatorname{first}\ (l\ (\lambda x.\lambda p.\operatorname{pair}\ (\operatorname{second}\ p)\ (\operatorname{cons}\ x\ (\operatorname{second}\ p)))\ (\operatorname{pair}\ \operatorname{nil} \operatorname{nil})) $$

Translation with other representations
Most real-world languages have support for machine-native integers; the church and unchurch functions convert between nonnegative integers and their corresponding church numerals. The functions are given here in Haskell, where the  corresponds to the λ of Lambda calculus. Implementations in other languages are similar.

Links

 * Lambda Calculus