Wikipedia / Church encoding
Wikipedia / Church encoding
In mathematics, Church encoding is a way of representing various types of data in the lambda calculus.
In the untyped lambda calculus the only primitive data type are functions, represented by lambda abstraction terms. Types that are usually considered primitive in other notations (such as integers, Booleans, pairs, lists, and tagged unions) are not natively present.
Hence the need arises to have ways to represent the data of these varying types by lambda terms, that is, by functions that are taking functions as their arguments and are returning functions as their results.
The Church numerals are a representation of the natural numbers using lambda notation. The method is named for Alonzo Church, who first encoded data in the lambda calculus this way. It can also be extended to represent other data types in the similar spirit.
This article makes occasional use of the alternative syntax for lambda abstraction terms, where λx.λy.λz.N is abbreviated as λxyz.N, as needed.
Church pairs
Church pairs are the Church encoding of the pair (two-tuple) type. To have two things is to be able to supply them to any observer expecting of two things. The pair is thus represented as a function that takes a function argument. The pair itself does not decide what to do with the elements of the tuple. When given its argument it will apply the argument to the two components of the pair. The definition of the pair constructor, the select first element and the select second element functions in lambda calculus are,
For example,
Church Booleans
Church Booleans encode the Boolean values true and false. Some programming languages use these as an implementation model for Boolean arithmetic; examples are Smalltalk and Pico.
Boolean logic embodies a choice between two alternatives. Thus the Church encodings of true and false are functions of two parameters:
true chooses the first parameter ;
false chooses the second parameter.
The two definitions in lambda calculus are:
These definitions allow predicates (i.e. functions returning logical values) to directly act as if-test clauses, so that if operator is just an identity function, and thus can be omitted. Each logical value already acts as an if, performing a choice between its two arguments. A Boolean value applied to two values returns either the first or the second value. The expression
returns then-clause if test-clause is true, and else-clause if test-clause is false.
Because logical values like true and false choose their first or second argument, they can be combined to provide logical operators. Several implementations are usually possible, whether by directly manipulating parameters or by reducing to the more basic logical values. Here are the definitions, using the shortened notation as mentioned at the start of the article (p,q are predicates; a,b are general values):
Some examples:
Optional values
An Optional value is represented as
, in case of "success," is a handler function which is presented with that value.
.
The user of an optional value has no way of knowing which case it is except by supplying it with the two arguments, one for each of the two possible cases.
Church numerals
Church numerals are the representations of natural numbers under Church encoding. The higher-order function that represents natural number n to its n-fold composition. In simpler terms, a numeral represents the number by applying any given function that number of times in sequence, starting from any given starting value:
Church encoding is thus a unary encoding of natural numbers, corresponding to simple counting. Each Church numeral achieves this by construction.
All Church numerals are functions that take two parameters. Church numerals 0, 1, 2, ..., are defined as follows in the lambda calculus:
Starting with 0 not applying the function at all, proceed with 1 applying the function once, 2 applying the function twice in a row, 3 applying the function three times in a row, etc.:
The Church numeral 3 is a chain of three applications of any given function in sequence, starting from some value. The supplied function is first applied to a supplied argument and then successively to its own result. The end result is not the number 3 (unless the supplied parameter happens to be 0 and the function is a successor function). The function itself, and not its end result, is the Church numeral 3. The Church numeral 3 means simply to do something three times. It is an ostensive demonstration of what is meant by "three times".
Calculation with Church numerals
Arithmetic operations on numbers produce numbers as their results. In Church encoding, these operations are represented by lambda abstractions which, when applied to Church numerals representing the operands, beta-reduce to the Church numerals representing the results.
:
, is obtained by β-reducing":
:
, and so by the virtue of Church encoding expressing the n is given by
:
is chosen as a mnemonic for "recursive result." This definition employs an additional argument to use the state-passing paradigm, since lambda calculus lacks mutation (so nothing can be changed, only replaced). See below for the detailed explanation.
This suggests implementing e.g. halving and factorial functions in the similar state-passing fashion,
.
, is expressed by repeated application of the predecessor operation a given number of times, just like addition can be expressed by repeated application of the successor operation a given number of times, etc.:
is the tetration is k th arrow in general.
Similarly to the factorial definition above, tetration can also be defined by using the intrinsic properties of the Church encoding, creating the "code" expression for it and letting the Church numerals themselves do the rest:
.
Direct subtraction and division
Just as addition as repeated successor has its counterpart in the direct style, so can subtraction be expressed directly and more efficiently as well:
.
:
Direct definition of division is given quite similarly as
steps.
can also be used in each of the three definitions above.
Table of functions on Church numerals
Notes:
Predecessor function
The predecessor function is given as
This encoding essentially uses the identity
or
An explanation of pred
, as usual, the only thing it can do is to apply that numeral to the two arguments, somehow modified so that the n in the chain replaced by the identity function:
.
along from the outside in, while modifying it as necessary, with the definitions
definition's lambda expression.
Now it is easy enough to see that
i.e. by eta-contraction and then by induction, it holds that
and so on.
Defining pred through pairs
The identity above may be coded with the explicit use of pairs. It can be done in several ways, for instance,
is:
This is a simpler definition to devise but leads to a more complex lambda expression,
definition. Another encoding follows the second variant of the predecessor identity directly,
definition here. Indeed if we trace its execution we arrive at the new, even more streamlined, yet fully equivalent, definition
which makes it fully clear and apparent that this is all about just argument modification and passing. Its reduction proceeds as
is short-circuiting. The top-down approach is also used with other definitions like
Division via general recursion
Division of natural numbers may be implemented by,
takes many beta reductions. Unless doing the reduction by hand, this doesn't matter that much, but it is preferable to not have to do this calculation twice (unless the direct subtraction definition is used, see above). The simplest predicate for testing numbers is IsZero so consider the condition.
. If this expression is used then the mathematical definition of division given above is translated into function on Church numerals as,
.
This problem may be corrected by adding 1 to n before calling divide. The definition of divide is then,
divide1 is a recursive definition. The Y combinator may be used to implement the recursion. Create a new function called div by;
to get,
Then,
where,
Gives,
Or as text, using \ for ,
divide = (\n.((\f.(\x.x x) (\x.f (x x))) (\c.\n.\m.\f.\x.(\d.(\n.n (\x.(\a.\b.b)) (\a.\b.a)) d ((\f.\x.x) f x) (f (c d m f x))) ((\m.\n.n (\n.\f.\x.n (\g.\h.h (g f)) (\u.x) (\u.u)) m) n m))) ((\n.\f.\x. f (n f x)) n))For example, 9/3 is represented by
divide (\f.\x.f (f (f (f (f (f (f (f (f x))))))))) (\f.\x.f (f (f x)))Using a lambda calculus calculator, the above expression reduces to 3, using normal order.
\f.\x.f (f (f (x)))Predicates
A predicate otherwise:
The following predicate tests whether the first argument is less-than-or-equal-to the second:
Because of the identity
the test for equality can be implemented as
In programming languages
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.
Signed numbers
One simple approach for extending Church Numerals to signed numbers is to use a Church pair, containing Church numerals representing a positive and a negative value. The integer value is the difference between the two Church numerals.
A natural number is converted to a signed number by,
Negation is performed by swapping the values.
The integer value is more naturally represented if one of the pair is zero. The OneZero function achieves this condition,
The recursion may be implemented using the Y combinator,
Plus and minus
Addition is defined mathematically on the pair by,
The last expression is translated into lambda calculus as,
Similarly subtraction is defined,
giving,
Multiply and divide
Multiplication may be defined by,
The last expression is translated into lambda calculus as,
A similar definition is given here for division, except in this definition, one value in each pair must be zero (see OneZero above). The divZ function allows us to ignore the value that has a zero component.
divZ is then used in the following formula, which is the same as for multiplication, but with mult replaced by divZ.
Rational and real numbers
Rational and computable real numbers may also be encoded in lambda calculus. Rational numbers may be encoded as a pair of signed numbers. Computable real numbers may be encoded by a limiting process that guarantees that the difference from the real value differs by a number which may be made as small as we need. The references given describe software that could, in theory, be translated into lambda calculus. Once real numbers are defined, complex numbers are naturally encoded as a pair of real numbers.
The data types and functions described above demonstrate that any data type or calculation may be encoded in lambda calculus. This is the Church–Turing thesis.
List encodings
A list contains some items in order. The basic operations on lists are:A representation of lists should provide ways to implement these operations. Some of these operations can be defined in terms of others, like
, many more functions can be defined in terms of structural recursion (right fold, i.e. catamorphism, also paramorphism, etc.), or general recursion using fixpoint recursion.
The archetypal lambda calculus representation of lists is Church List encoding. It represents lists as right folds, as functions to return the results of folding over the list with user-supplied arguments.
It follows the paradigm of "a thing is something that can be observed". No matter the concrete implementation, folding a given list of values results in the same result. This provides an abstract view at what a list is. Church List encoding is such a mechanism.
On the other hand, seen more concretely, lists can be represented as a sequence of linked list nodes. Two possible encodings use either two Church pairs for each list node, or just one pair per each node.
Seen as an algebraic data type, lists are represented via Scott encoding.
Church lists – right fold representation
This is the original Church encoding for lists. A list is represented by a binary function, that, when supplied with two arguments, – a "combining function" and a "sentinel value", – will perform the right fold of the encoded list using those two arguments.
For an empty list the sentinel value is returned as the folding's result. The result of folding a non-empty list with head h and tail t is the result of combining, by the supplied function, of the head h with the result of folding the tail t with the supplied two arguments. Thus the combining function's two arguments are, conceptually, the current element and the result of folding the rest of the list.
For example, a list of three elements x, y and z is represented by a term that when applied to c and n returns c x (c y (c z n)). Equivalently, it is an application of the chain of functional compositions ) of partial applications, ((c x (c y (c z)) n.
This encoding follows the following logic: the equations
foldr c n [ ] = n
foldr c n [x ] = c x n
foldr c n [x,y,z] = c x (foldr c n [y,z])
= c x (c y (c z n))mean that
.
Since Church encoded list is its own folding function, folding it just means applying that function to the supplied arguments.
This list representation can be given type in System F.
f) n, as is used in Church numerals.
Two pairs as a list node
A nonempty list can be represented by a Church pair, where
first contains the list's head
second contains the list's tail
However this does not give a representation of the empty list, because there is no "null" pointer. To represent null, the pair can be wrapped in another pair, giving three values:
first - the null list indicator (a Boolean).
first of second contains the head (car).
second of second contains the tail (cdr).
Using this idea the basic list operations can be defined like this:In a nil node second is never accessed, provided that head and tail are only applied to nonempty lists.
One pair as a list node
Alternatively, define
being discarded, as an artificial device:
Other operations in this encoding are:
For some of the definitions the general fold-based ones (found below) are efficient enough.
It is important for the successor function on Church numerals to be defined in the lazy fashion, , as opposed to , so that the function produces lazy numerals, for overall most lazy operation of reductions under the topmost leftmost reduction strategy. In other words, we don't need to know the final length value if all we need to know is whether it is non-zero or not.
Scott lists
Scott encoding for data types follows their surface syntax without regard to recursion in the data type. In the disjunction of conjunctions a.k.a. sum of products style of algebraic data type definitions, it represents a given datum as a function which expects as many arguments as there are alternatives in its data type definition, where each such argument is expected to be a "handler" function that must be able to handle the given number of data arguments that will correspond to the data fields for that alternative.
Given all the handlers as arguments, the datum representation function will then call the appropriate handler with the corresponding internal data. Scott encoded values can thus be said to embody the pattern matching case handling for their data type.
For lists, it means the data type definition of
and lists being represented as
. One such example is foldr, unlike the no-op that it is under Church encoding. But tail is immediately available, and so its definition is much simpler here, in comparison. See Scott encoding for more.
Scott encoding can be seen as using the idea of continuations, which can lead to simpler code. In this approach, we use the fact that lists can be observed using pattern matching expression. For example, using Scala notation, if list denotes a value of type List with empty list Nil and constructor Cons(h, t) we can inspect the list and compute nilCode in case the list is empty and when the list is not empty: The is given by how it acts upon and . We therefore define a list as a function that accepts such and as arguments, so that instead of the above pattern match we may simply write:
Let us denote by the parameter corresponding to and by the parameter corresponding to . The empty list is then the one that returns the nil argument:
The non-empty list with head and tail is given by
More generally, an algebraic data type arguments as well.
Scott encoding can be done in untyped lambda calculus, whereas its use with types requires a type system with recursion and type polymorphism. A list with element type E in this representation that is used to compute values of type C would have the following recursive type definition, where '=>' denotes function type: A list that can be used to compute arbitrary types would have a type that quantifies over C. A list generic in E would also take E as the type argument.
General list manipulation
Whatever the representation in use, a plethora of general functions can be defined using just the few basic definitions, as mentioned at the top of this section, in terms of structural recursion via right fold i.e. catamorphism, or using paramorphism, etc.; or with general recursion via fixed points.
definition. The more specific definitions provided by the specific encodings can be more efficient.
General remarks
is the size of the data structure, making Church encoding impractical. Research has shown that this can be addressed by targeted optimizations, but most functional programming languages instead expand their intermediate representations to contain algebraic data types. Nonetheless Church encoding is often used in theoretical arguments, as it is a natural representation for partial evaluation and theorem proving. Operations can be typed using higher-ranked types, and primitive recursion is easily accessible. The assumption that functions are the only primitive data types streamlines many proofs.
Church encoding is complete but only representationally. Additional functions are needed to translate the representation into common data types, for display to people. It is not possible in general to decide if two functions are extensionally equal due to the undecidability of equivalence from Church's theorem. The translation may apply the function in some way to retrieve the value it represents, or look up its value as a literal lambda term. Lambda calculus is usually interpreted as using intensional equality. There are potential problems with the interpretation of results because of the difference between the intensional and extensional definition of equality.
See also
System F for Church numerals in a typed calculus
Von Neumann definition of ordinals — another way to encode natural numbers: as sets
References
All about Church and other similar encodings, including how to derive them and operations on them, from first principles