906 words
5 minutes
Lambda Calculus via C# (19) Church Encoding, And More

[LINQ via C# series]#

[Lambda Calculus via C# series]#

So far a ton has been encoded. Here is a summary.

Summary of church encoding#

Boolean#

True := λt.λf.t
False := λt.λf.f

Boolean logic#

And := λa.λb.a b False
Or := λa.λb.a True b
Not := λb.b False True
Xor := λa.λb.a (b False True) (b True False)

If logic#

If := λc.λt.λf.c t f (λx.x)

Numeral#

0 := λfx.x ≡ λf.λx.x ≡ λf.λx.f0 x
1 := λfx.f x ≡ λf.λx.f x ≡ λf.λx.f1 x
2 := λfx.f (f x) ≡ λf.λx.(f ∘ f) x ≡ λf.λx.f2 x
3 := λfx.f (f (f x)) ≡ λf.λx.(f ∘ f ∘ f) x ≡ λf.λx.f3 x
...
n := λfx.f (f ... (f x)...) ≡ λf.λx.(f ∘ f ∘ ... ∘ f) x ≡ λf.λx.fn x

Arithmetic#

Increase := λn.λf.λx.f (n f x)
Increase2 := λn.λf.f ∘ (n f)
Add := λa.λb.λf.λx.a f (b f x)
Add2 := λa.λb.λf.fa ∘ fb ≡ λa.λb.λf.(a f) ∘ (b f)
Add3 := λa.λb.a Increase b
Decrease := λn.λf.λx.n (λg.λh.h (g f)) (λu.x) (λu.u)
Decrease2 := λn.Item1 (n (Shift Increase) (CreateTuple 0 0))
Subtract := λa.λb.b Decrease a
Multiply := λa.λb.a (λx.Add b x) 0
_DivideBy := λa.λb.If (IsGreaterOrEqual a b) (λx.Add One (_DivideBy (Subtract a b) b)) (λx.Zero)
DivideByIgnoreZero = λa.λb.If (IsZero b) (λx.0) (λx._DivideBy a b)
Pow := λm.λ e.e (λx.Multiply m x) 1

A better DivideBy will be re-implemented after introducing Y combinator:

DivideBy := Y (λf.λa.λb.If (IsGreaterOrEqual a b) (λx.Add One (f (Subtract a b) b)) (λx.Zero))
≡ (λf.(λx.f (x x)) (λx.f (x x))) (λf.λa.λb.If (IsGreaterOrEqual a b) (λx.Add One (f (Subtract a b) b)) (λx.Zero))

So DivideByIgnoreZero can by redefined using DivideBy instead of _DivideBy:

DivideByIgnoreZero = λa.λb.If (IsZero b) (λx.0) (λx.DivideBy a b)

Predicate#

IsZero := λn.n (λx.False) True

Comparison#

IsLessOrEqual := λa.λb.IsZero (Subtract a b)
IsGreaterOrEqual := λa.λb.IsZero (Subtract b a)
IsEqual := λa.λb.And (IsLessOrEqual a b) (IsGreaterOrEqual a b)
IsLess := λa.λb.Not (IsGreaterOrEqual a b)
IsGreater := λa.λb.Not (IsLessOrEqual a b)
IsNotEqual := λa.λb.Not (IsEqual a b)

Pair (2-tuple)#

CreateTuple := λx.λy.λf.f x y
Tuple := λf.f x y
Item1 := λt.t True
Item2 := λt.t False
Shift := λf.λt.CreateTuple (Item2 t) (f (Item1 t))
Swap := λt.CreateTuple (Item2 t) (Item1 t)

List#

1 pair for each node, and null#

CreateListNode := CreateTuple ≡ λv.λn.λf.f v n
Value := Item1 ≡ λl.l (λv.λn.v)
Next := Item2 ≡ λl.l (λv.λn.n)
Null := False
IsNull := λl.l (λv.λn.λx.False) True
Index := λl.λi.i Next l

2 pairs for each node, and null#

CreateListNode2 := λv.λn.CreateTuple False (CreateTuple v n)
Value2 := λl.Item1 (Item2 l)
Next2 := λl.If (IsNull2 l) (λx.l) (λx.(Item2 (Item2 l)))
Null2 := λf.True
IsNull2 := λl.(Item1 l)
Index2 := λl.λi.i Next2 l

Fold (aggregate) function for each node, and null#

CreateListNode3 := λv.λn.λf.λx.f v (n f x)
Value3 := λl.λx.l (λv.λy.v) x
Next3 := λl.Item2 (l (λv.λt.ShiftTuple (CreateListNode3 v)) (CreateTuple Null3 Null3))
Null3 := λf.λx.x
IsNull3 := λl.l (λv.λx.False) True
Index3 := λl.λi.i Next3 l

Signed number#

Signed := Tuple
ToSigned := λn.CreateTuple n 0
Negate := Swap
Positive := Item1
Negative := Item2
FormatWithZero := λs.If (IsEqual sp sn) (λx.ToSigned 0) (λx.If (IsGreater sp sn) (λy.ToSigned (Subtract sp sn)) (λy.Negate (ToSigned (Subtract sn sp))))

Arithmetic#

AddSigned := λa.λb.FormatWithZero (CreateTuple (Add ap bp) (Add an bn))
SubtractSigned := λa.λb.FormatWithZero (CreateTuple (Add ap bn) (Add an bp))
MultiplySigned := λa.λb.FormatWithZero (CreateTuple (Add (Multiply ap bp) (Multiply an bn)) (Add (Multiply ap bn) (Multiply an bp)))
DivideBySigned := λa.λb.FormatWithZero (CreateTuple (Add (DivideByIgnoreZero ap bp) + (DivideByIgnoreZero an bn)) (Add (DivideByIgnoreZero ap bn) (DivideByIgnoreZero an bp))))

Encode, encode, and encode#

From signed number to complex integer and rational number#

With signed number, complex integer can be encoded by a Church pair of signed numbers: (sreal, simaginary), which represents complex integer z = sreal + simaginary * i.

With signed number, rational number can also be encoded by a Church pair of a signed number and a Church numeral: (snumerator, ndenominator), which represents rational number q = snumerator / (1 + ndenominator).

Dyadic rational number can be encoded by (snumerator, nexponent) as well, which represents d = snumerator / (2 ^ nexponent).

From rational number to real number and complex number#

Then with rational number, a real number r can be encoded in many different ways:

  • r can be represented by a sequence of Church pair of 2 rational numbers p0 = (q0, q0’), p1 = (q1, q1’), p2 = (q2, q2’), …, such that:
    • pn represents a rational interval, since qn and qn’ are both rational numbers.
    • pn + 1 ⊆ pn
    • limn → ∞ qn’ − qn = 0
    • r = ∩n ∈ N pn
  • r can be represented by a Cauchy sequence of rational numbers q0, q1, q2, …, and a function f of type Func<_Numeral, _Numeral>, defining the convergence rate of the Cauchy sequence such that:
    • ∀i.j.k. | qf(i) + j - qf(i) + k | ≤ 2-i
    • r = limn → ∞ qn
  • r can be represented by a Cauchy sequence of rational numbers q0, q1, q2, … with a fixed convergence rate, such that:
    • ∀i.j. | qi - qi + j | ≤ 1 / i
    • r = limn → ∞ qn

etc.. An example in Haskell can be found on Github.

With real number, complex number can be naturally encoded by a Church pair of 2 real numbers (rreal, rimaginary), which represents complex number z = rreal + rimaginary * i.

And much more#

Church pair can encode more complex data structures, like tree.

Church List can encode string.

Church Tuple and Church List can encode more complex algebra types.

Don’t worry. The encoding stops here. All the above data types and functions demonstrate that any data type or calculation may be encoded in lambda calculus. This is the Church-Turing thesis.

Lambda Calculus via C# (19) Church Encoding, And More
https://dixin.github.io/posts/lambda-calculus-via-c-sharp-19-church-encoding-and-more/
Author
Dixin
Published at
2018-11-19
License
CC BY-NC-SA 4.0