Church encoding

From testwiki
Jump to navigation Jump to search

Template:Short description Template:Lead too short In mathematics, Church encoding is a means of representing data and operators in the lambda calculus. 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.

Terms that are usually considered primitive in other notations (such as integers, Booleans, pairs, lists, and tagged unions) are mapped to higher-order functions under Church encoding. The Church–Turing thesis asserts that any computable operator (and its operands) can be represented under Church encoding.Template:Dubious In the untyped lambda calculus the only primitive data type is the function.

Use

A straightforward implementation of Church encoding slows some access operations from O(1) to O(n), where n is the size of the data structure, making Church encoding impractical.[1] 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.[2] Nonetheless Church encoding is often used in theoretical arguments, as it is a natural representation for partial evaluation and theorem proving.[1] Operations can be typed using higher-ranked types,[3] and primitive recursion is easily accessible.[1] 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.

Church numerals

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.

fn=fffn times.

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, 3 applying the function three times, etc.:

NumberFunction definitionLambda expression00 f x=x0=λf.λx.x11 f x=f x1=λf.λx.f x22 f x=f (f x)2=λf.λx.f (f x)33 f x=f (f (f x))3=λf.λx.f (f (f x))nn f x=fn xn=λf.λx.fn x

The Church numeral 3 represents the action of applying any given function three times to a value. The supplied function is first applied to a supplied parameter and then successively to its own result. The end result is not the numeral 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 anything three times. It is an ostensive demonstration of what is meant by "three times".

Calculation with Church numerals

Arithmetic operations on numbers may be represented by functions on Church numerals. These functions may be defined in lambda calculus, or implemented in most functional programming languages (see converting lambda expressions to functions).

The addition function plus(m,n)=m+n uses the identity f(m+n)(x)=fm(fn(x)).

plusλm.λn.λf.λx.m f (n f x)

The successor function succ(n)=n+1 is β-equivalent to (plus 1).

succλn.λf.λx.f (n f x)

The multiplication function mult(m,n)=m*n uses the identity f(m*n)(x)=(fn)m(x).

multλm.λn.λf.λx.m (n f) x

The exponentiation function exp(b,n)=bn is given by the definition of Church numerals, n h x=hn x. In the definition substitute hb,xf to get n b f=bn f and,

exp b n=bn=n b

which gives the lambda expression,

expλb.λn.n b

The pred(n) function is more difficult to understand.

predλn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)

A Church numeral applies a function n times. The predecessor function must return a function that applies its parameter n - 1 times. This is achieved by building a container around f and x, which is initialized in a way that omits the application of the function the first time. See predecessor for a more detailed explanation.

The subtraction function can be written based on the predecessor function.

minusλm.λn.(npred) m

Table of functions on Church numerals

Function Algebra Identity Function definition Lambda expressions
Successor n+1 fn+1 x=f(fnx) succ n f x=f (n f x) λn.λf.λx.f (n f x) ...
Addition m+n fm+n x=fm(fnx) plus m n f x=m f (n f x) λm.λn.λf.λx.m f (n f x) λm.λn.nsuccm
Multiplication m*n fm*n x=(fm)n x multiply m n f x=m (n f) x λm.λn.λf.λx.m (n f) x λm.λn.λf.m (n f)
Exponentiation bn n b f=bn fTemplate:Efn exp b n f x=n b f x λb.λn.λf.λx.n b f x λb.λn.n b
PredecessorTemplate:Efn n1 incncon=val(fn1x) if(n==0) 0 else (n1)

λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)

SubtractionTemplate:Efn (Monus) mn fmn x=(f1)n(fmx) minus m n=(npred) m ... λm.λn.npredm

Notes: Template:Notelist

Derivation of predecessor function

The predecessor function used in the Church encoding is,

pred(n)={0if n=0,n1otherwise.

We need a way of applying the function 1 fewer times to build the predecessor. A numeral Template:Mvar applies the function Template:Mvar Template:Mvar times to Template:Mvar. The predecessor function must use the numeral Template:Mvar to apply the function Template:Math times.

Before implementing the predecessor function, here is a scheme that wraps the value in a container function. We will define new functions to use in place of Template:Mvar and Template:Mvar, called Template:Math and Template:Math. The container function is called Template:Math. The left-hand side of the table shows a numeral Template:Mvar applied to Template:Math and Template:Math.

NumberUsing initUsing const0init=value x1inc init=value (f x)inc const=value x2inc (inc init)=value (f (f x))inc (inc const)=value (f x)3inc (inc (inc init))=value (f (f (f x)))inc (inc (inc const))=value (f (f x))nninc init=value (fn x)=value (n f x)ninc const=value (fn1 x)=value ((n1) f x)

The general recurrence rule is,

inc (value v)=value (f v)

If there is also a function to retrieve the value from the container (called Template:Math),

extract (value v)=v

Then Template:Math may be used to define the Template:Math function as,

samenum=λn.λf.λx.extract (nincinit)=λn.λf.λx.extract (value (n f x))=λn.λf.λx.n f x=λn.n

The Template:Math function is not intrinsically useful. However, as Template:Math delegates calling of Template:Mvar to its container argument, we can arrange that on the first application Template:Math receives a special container that ignores its argument allowing to skip the first application of Template:Mvar. Call this new initial container Template:Math. The right-hand side of the above table shows the expansions of Template:Mvar Template:Math Template:Math. Then by replacing Template:Math with Template:Math in the expression for the Template:Math function we get the predecessor function,

pred=λn.λf.λx.extract (nincconst)=λn.λf.λx.extract (value ((n1) f x))=λn.λf.λx.(n1) f x=λn.(n1)

As explained below the functions Template:Math, Template:Math, Template:Math, Template:Math and Template:Math may be defined as,

value=λv.(λh.h v)extractk=k λu.uinc=λg.λh.h (g f)init=λh.h xconst=λu.x

Which gives the lambda expression for Template:Math as,

pred=λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)

Value container

The value container applies a function to its value. It is defined by,

value v h=h v

so,

value=λv.(λh.h v)

Inc

The Template:Math function should take a value containing Template:Mvar, and return a new value containing Template:Mvar.

inc (value v)=value (f v)

Letting g be the value container,

g=value v

then,

g f=value v f=f v

so,

inc g=value (g f)
inc=λg.λh.h (g f)

Extract

The value may be extracted by applying the identity function,

I=λu.u

Using Template:Mvar,

value v I=v

so,

extract k=k I

Const

To implement Template:Math the Template:Math function is replaced with the Template:Math that does not apply Template:Mvar. We need Template:Math to satisfy,

inc const=value x
λh.h (const f)=λh.h x

Which is satisfied if,

const f=x

Or as a lambda expression,

const=λu.x

Another explanation of pred

A much simpler presentation is enabled using combinators notation.

Kxy=x
Ix=x
Xfri=i(rf)
Pred nfx=n(Xf)(Kx)I

Now it is easy enough to see that

Pred (Succ n)fx=Succ n(Xf)(Kx)I=Xf(n(Xf)(Kx))I
    =I(n(Xf)(Kx)f)= ... =I(f(f...(f(Kxf))...))=I(nfx)=nfx
Pred Zero fx= Zero(Xf)(Kx)I= KxI= x= Zero fx

i.e. by eta-contraction, and then by induction,

Pred (Succ n)=n
Pred Zero=Zero
Pred (Pred Zero)= Pred Zero= Zero

etc.

Another way of defining pred

Pred may also be defined using pairs:

f= λp. pair (second p) (succ (second p))zero= (λf.λx. x)pc0= pair zero zeropred= λn. first (n f pc0)

This is a simpler definition but leads to a more complex expression for pred. The expansion for predthree:

predthree= first (f (f (f (pair zero zero))))= first (f (f (pair zero one)))= first (f (pair one two))= first (pair two three)= two

Division

Division of natural numbers may be implemented by,[4]

n/m=if nm then 1+(nm)/m else 0

Calculating nm 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. The simplest predicate for testing numbers is IsZero so consider the condition.

IsZero (minus n m)

But this condition is equivalent to nm, not n<m. If this expression is used then the mathematical definition of division given above is translated into function on Church numerals as,

divide1 n m f x=(λd.IsZero d (0 f x) (f (divide1 d m f x))) (minus n m)

As desired, this definition has a single call to minus n m. However the result is that this formula gives the value of (n1)/m.

This problem may be corrected by adding 1 to n before calling divide. The definition of divide is then,

divide n=divide1 (succ n)

divide1 is a recursive definition. The Y combinator may be used to implement the recursion. Create a new function called div by;

  • In the left hand side divide1div c
  • In the right hand side divide1c

to get,

div=λc.λn.λm.λf.λx.(λd.IsZero d (0 f x) (f (c d m f x))) (minus n m)

Then,

divide=λn.divide1 (succ n)

where,

divide1=Y divsucc=λn.λf.λx.f (n f x)Y=λf.(λx.f (x x)) (λx.f (x x))0=λf.λx.xIsZero=λn.n (λx.false) true
trueλa.λb.afalseλa.λb.b
minus=λm.λn.npredmpred=λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)

Gives,

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)

Or as text, using \ for Template:Mvar,

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

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.[5] The integer value is the difference between the two Church numerals.

A natural number is converted to a signed number by,

converts=λx.pair x 0

Negation is performed by swapping the values.

negs=λx.pair (second x) (first x)

The integer value is more naturally represented if one of the pair is zero. The OneZero function achieves this condition,

OneZero=λx.IsZero (first x) x (IsZero (second x) x (OneZero (pair (pred (first x)) (pred (second x)))))

The recursion may be implemented using the Y combinator,

OneZ=λc.λx.IsZero (first x) x (IsZero (second x) x (c (pair (pred (first x)) (pred (second x)))))
OneZero=YOneZ

Plus and minus

Addition is defined mathematically on the pair by,

x+y=[xp,xn]+[yp,yn]=xpxn+ypyn=(xp+yp)(xn+yn)=[xp+yp,xn+yn]

The last expression is translated into lambda calculus as,

pluss=λx.λy.OneZero (pair (plus (first x) (first y)) (plus (second x) (second y)))

Similarly subtraction is defined,

xy=[xp,xn][yp,yn]=xpxnyp+yn=(xp+yn)(xn+yp)=[xp+yn,xn+yp]

giving,

minuss=λx.λy.OneZero (pair (plus (first x) (second y)) (plus (second x) (first y)))

Multiply and divide

Multiplication may be defined by,

x*y=[xp,xn]*[yp,yn]=(xpxn)*(ypyn)=(xp*yp+xn*yn)(xp*yn+xn*yp)=[xp*yp+xn*yn,xp*yn+xn*yp]

The last expression is translated into lambda calculus as,

mults=λx.λy.pair (plus (mult (first x) (first y)) (mult (second x) (second y))) (plus (mult (first x) (second y)) (mult (second x) (first y)))

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=λx.λy.IsZero y 0 (divide x y)

divZ is then used in the following formula, which is the same as for multiplication, but with mult replaced by divZ.

divides=λx.λy.pair (plus (divZ (first x) (first y)) (divZ (second x) (second y))) (plus (divZ (first x) (second y)) (divZ (second x) (first y)))

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.[6] [7] 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.

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.

type Church a = (a -> a) -> a -> a

church :: Integer -> Church Integer
church 0 = \f -> \x -> x
church n = \f -> \x -> f (church (n-1) f x)

unchurch :: Church Integer -> Integer
unchurch cn = cn (+ 1) 0

Church Booleans

Church Booleans are the Church encoding of 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 may be considered as a choice. The Church encoding of true and false are functions of two parameters:

  • true chooses the first parameter.
  • false chooses the second parameter.

The two definitions are known as Church Booleans:

trueλa.λb.afalseλa.λb.b

This definition allows predicates (i.e. functions returning logical values) to directly act as if-clauses. A function returning a Boolean, which is then applied to two parameters, returns either the first or the second parameter:

predicate-x then-clause else-clause

evaluates to then-clause if predicate-x evaluates to true, and to else-clause if predicate-x evaluates to false.

Because true and false choose the first or second parameter they may be combined to provide logic operators. Note that there are multiple possible implementations of not.

and=λp.λq.p q por=λp.λq.p p qnot1=λp.λa.λb.p b anot2=λp.p (λa.λb.b) (λa.λb.a)=λp.pfalsetruexor=λa.λb.a (not b) bif=λp.λa.λb.p a b

Some examples:

andtruefalse=(λp.λq.p q p) true false=truefalsetrue=(λa.λb.a)falsetrue=falseortruefalse=(λp.λq.p p q) (λa.λb.a) (λa.λb.b)=(λa.λb.a) (λa.λb.a) (λa.λb.b)=(λa.λb.a)=truenot1 true=(λp.λa.λb.p b a)(λa.λb.a)=λa.λb.(λa.λb.a) b a=λa.λb.(λc.b) a=λa.λb.b=falsenot2 true=(λp.p (λa.λb.b)(λa.λb.a))(λa.λb.a)=(λa.λb.a)(λa.λb.b)(λa.λb.a)=(λb.(λa.λb.b)) (λa.λb.a)=λa.λb.b=false

Predicates

A predicate is a function that returns a Boolean value. The most fundamental predicate is IsZero, which returns true if its argument is the Church numeral 0, and false if its argument is any other Church numeral:

IsZero=λn.n (λx.false) true

The following predicate tests whether the first argument is less-than-or-equal-to the second:

LEQ=λm.λn.IsZero (minus m n),

Because of the identity,

x=y(xyyx)

The test for equality may be implemented as,

EQ=λm.λn.and (LEQ m n) (LEQ n m)

Church pairs

Template:See also

Church pairs are the Church encoding of the pair (two-tuple) type. 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. The definition in lambda calculus is,

pairλx.λy.λz.z x yfirstλp.p (λx.λy.x)secondλp.p (λx.λy.y)

For example,

first (pair a b)=(λp.p (λx.λy.x)) ((λx.λy.λz.z x y) a b)=(λp.p (λx.λy.x)) (λz.z a b)=(λz.z a b) (λx.λy.x)=(λx.λy.x) a b=a

List encodings

An (immutable) list is constructed from list nodes. The basic operations on the list are;

Function Description
nil Construct an empty list.
isnil Test if list is empty.
cons Prepend a given value to a (possibly empty) list.
head Get the first element of the list.
tail Get the rest of the list.

We give four different representations of lists below:

  • Build each list node from two pairs (to allow for empty lists).
  • Build each list node from one pair.
  • Represent the list using the right fold function.
  • Represent the list using Scott's encoding that takes cases of match expression as arguments

Two pairs as a list node

A nonempty list can be implemented by a Church pair;

  • First contains the head.
  • Second contains the tail.

However this does not give a representation of the empty list, because there is no "null" pointer. To represent null, the pair may be wrapped in another pair, giving three values:

  • First - the null pointer (empty list).
  • Second.First contains the head.
  • Second.Second contains the tail.

Using this idea the basic list operations can be defined like this:[8]

Expression Description
nilpair true true The first element of the pair is true meaning the list is null.
isnilfirst Retrieve the null (or empty list) indicator.
consλh.λt.pairfalse (pairh t) Create a list node, which is not null, and give it a head h and a tail t.
headλz.first (secondz) second.first is the head.
tailλz.second (secondz) second.second is the tail.

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[9]

conspairheadfirsttailsecondnilfalseisnilλl.l(λh.λt.λd.false)true

where the last definition is a special case of the general

process-listλl.l(λh.λt.λd.head-and-tail-clause)nil-clause

Other operations for one pair as a list node

tail-or-nilλl. l (λh.λt.λd. t) nilfoldλf. Y (λr.λa.λl. l (λh.λt.λd. r (f a h) t) a)rfoldλf.λa. Y (λr.λl. l (λh.λt.λd. f (r t) h) a)lengthfold (λa.λh. succ a) zero

mapλf.λl. rfold (λa.λh. cons (f h) a) nil lλf. rfold (λa.λh. cons (f h) a) nilfilterλf.λl. rfold (λa.λh. f h (cons h a) a) nil lλf. rfold (λa.λh. f h (cons h a) a) nilreverseλl. fold (λa.λh. cons h a) nil lfold (λa.λh. cons h a) nilconcatλl.λg. rfold (λa.λh. cons h a) g lappendλl.λv. concat l (cons v nil)

skipλn.λl. n tail-or-nil lλn. n tail-or-nilY (λr.λn.λl. l (λh.λt.λd. IsZero n l (r (pred n) t)) nil)skip-lastλn.λl. IsZero n l second(    Y (λr.λlr. lr (λh.λt.λd.        r t (λna.λla. IsZero na            (pair zero (cons h la))            (pair (pred na) nil)        ))        (pair n nil)    ) l)skip-whileλf. Y (λr.λl. l (λh.λt.λd. f h (r t) l) nil)takeY (λr.λn.λl. l (λh.λt.λd. IsZero n nil (cons h (r (pred n) t))) nil)take-lastλn.λl. IsZero n l second(    Y (λr.λlr. lr (λh.λt.λd.        r t (λna.λla. IsZero na            (pair zero la)            (pair (pred na) lr)        ))        (pair n nil)    ) l)take-whileλf. Y (λr.λl. l (λh.λt.λd. f d (cons h (r t)) nil) nil)

allY (λr.λf.λl. l (λh.λt.λd. f h (r f t) false) true)anyY (λr.λf.λl. l (λh.λt.λd. f h true (r f t)) false)element-atλn.λl. head (skip n l)insert-atλn.λv.λl. concat (take n l) (cons v (skip n l))remove-atλn.λl. concat (take n l) (skip (succ n) l)replace-atλn.λv.λl. concat (take n l) (cons v (skip (succ n) l))index-ofλf. Y (λr.λn.λl. l (λh.λt.λd. f h n (r (succ n) t)) zero) onelast-index-ofλf. Y (λr.λn.λl. l (λh.λt.λd. (λi. IsZero i (f h n zero) i) (r (succ n) t)) zero) onerangeλf.λz. Y (λr.λs.λn. IsZero n nil (cons (s f z) (r (succ s) (pred n)))) zerorepeatλv. Y (λr.λn. IsZero n nil (cons v (r (pred n))))zipY (λr.λl1.λl2. l1 (λh1.λt1.λd1. l2 (λh2.λt2.λd2. cons (pair h1 h2) (r t1 t2)) nil) nil)

Represent the list using right fold

As an alternative to the encoding using Church pairs, 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 that when applied to a combinator c and a value 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.

nilλc.λn.nsingletonλh.λc.λn.c h nconsλh.λt.λc.λn.c h (t c n)appendλl.λt.λc.λn.l c (t c n)isnilλl.l (λh.λr.false) truenonemptyλl.l (λh.λr.true) falseheadλl.l (λh.λr.h) falsemapλf.λl.λc.λn.l (λh.λr.c (f h) r) ntailλl.λc.λn.l (λh.λr.λg.g h (r c)) (λc.n) (λh.λt.t)

This list representation can be given type in System F.

The evident correspondence to Church numerals is non-coincidental, as that can be seen as a unary encoding, with natural numbers represented by lists of unit (i.e. non-important) values, e.g. [() () ()], with the list's length serving as the representation of the natural number. Right folding over such lists uses functions which necessarily ignore the element's value, and is equivalent to the chained functional composition, i.e. (c () ∘ c () ∘ c ()) n = (f ∘ f ∘ f) n, as is used in Church numerals.

Represent the list using Scott encoding

An alternative representation is Scott encoding, which uses the idea of continuations and can lead to simpler code.[10] (see also Mogensen–Scott encoding).

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 Template:Code when the list is not empty:

list match {
  case Nil        => nilCode
  case Cons(h, t) => consCode(h,t)
}

The Template:Code is given by how it acts upon Template:Code and Template:Code. We therefore define a list as a function that accepts such Template:Code and Template:Code as arguments, so that instead of the above pattern match we may simply write:

list nilCode consCode

Let us denote by Template:Code the parameter corresponding to Template:Code and by Template:Code the parameter corresponding to Template:Code. The empty list is the one that returns the nil argument:

nilλn.λc. n

The non-empty list with head Template:Code and tail Template:Code is given by

cons h t    λn.λc. c h t

More generally, an algebraic data type with m alternatives becomes a function with m parameters. When the ith constructor has ni arguments, the corresponding parameter of the encoding takes ni 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:

type List = 
  C =>                    // nil argument
  (E => List => C) =>     // cons argument
  C                       // result of pattern matching

A list that can be used to compute arbitrary types would have a type that quantifies over C. A list generic Template:Clarification needed in E would also take E as the type argument.

See also

References

Template:Mathematical logic Template:Alonzo Church