====== Lambda Calculus as a programming language =====
===== Church Encodings ======
==== Booleans ====
We can encode boolean values **TRUE** and **FALSE** in lambda calculus as functions that take 2 values, **x** and **y**, and return the first (for **TRUE**) or second (for **FALSE**) value. \\
$ TRUE = \lambda x.\lambda y.y$ \\
$ FALSE = \lambda x.\lambda y.x$ \\
As we defined it, **TRUE** is sometimes called the **K**-Combinator (or //Kestrel//), and **FALSE** the **KI**-Combinator (or //Kite//).
Some common operation on booleans (that were discussed during the lecture) are: \\
\\
$ AND = \lambda x.\lambda y.((x \ y) \ x) $ \\
$ OR = \lambda x.\lambda y.((x \ x) \ y) $ \\
$ NOT = \lambda x.((x \ FALSE) \ TRUE) $ \\
**NOT** can also be written as: \\
\\
$ NOT = \lambda x.\lambda a.\lambda b.((x \ b) \ a) $ \\
\\
You can convince yourself that this works by evaluating $ NOT \ TRUE $ and $ NOT \ FALSE $. This way of writting **NOT** is also called the **C**-Combinator (or //Cardinal//).
\\
**1.** Define the $ XOR $ operation over booleans.
==== Pairs ====
We can also encode // data structures //. We will only look at one of the simpler ones, the **pair**. \\
A pair encapsulates two variables together, that we can later access using $ FIRST $ and $ SECOND $ . \\
\\
$ PAIR = \lambda x.\lambda y.\lambda f.f \ x \ y $ \\
$ FIRST = \lambda p.p \ TRUE $ \\
$ SECOND = \lambda p.p
\ FALSE $ \\
==== Natural Numbers ====
Church numerals represent natural numbers as **higher-order functions**. Under this representation, the number //**n**// is a function that maps **f** to its **n-fold composition**. \\
\\
$ N0 = \lambda f.\lambda x. x $ \\
$ N1 = \lambda f.\lambda x. (f \ x) $ \\
$ N2 = \lambda f.\lambda x. (f \ (f \ x)) $ \\
...
Does **N0** look familiar? It's the same as **FALSE** if you rename the variables (using $\alpha$-reduction).
You can also define operation on church numerals, some (that were discussed during the lecture) are: \\
\\
$ SUCC = \lambda n.\lambda f.\lambda x.(f \ ((n \ f) \ x)) $ \\
$ ISZERO = \lambda n.((n \lambda x.FALSE) \ TRUE) $ \\
$ ADD = \lambda n.\lambda m.\lambda f.\lambda x.((n \ f) ((m \ f) \ x)) $ \\
\\
**2.** Define multiplication under church numerals: $ MULT = \lambda n.\lambda m. \ ... $ (**Hint:** you can do it without the **Y**-Combinator)
**3.** Define exponentiation under church numerals: $ EXP = \lambda n.\lambda m. \ ... $
**4.** (*) Define the predecessor operator, that takes a number and returns the number prior to it.
What's the predecessor of 0? Evaluate $ PRED \ N0 $.
**Solution:** \\
If use pairs, we can define the // shift-and-increment // operator: \\
$ \phi' = \lambda x.PAIR \ x \ (SUCC \ x) $ \\
\\
This takes a number $ n $, and returns a pair made up of the number and it's succesor ( $ n $ , $ SUCC(n) $ ). \\
\\
To make this function be able to be iterate multiple times, we make the input another pair, where the second value is the 'real' input: \\
$ \phi = \lambda p.PAIR \ (SECOND \ p) \ (SUCC \ (SECOND \ p)) $ \\
\\
Now we can just iterate this **n** times starting with $ N0 $, and we get a pair ($ n - 1 $, $ n $), where the first value is our predecesor: \\
$ PRED = \lambda.n.FIRST \ (n \ \phi \ (PAIR \ N0 \ N0)) $ \\
\\
An alternative solution, that uses a value container is the following (unfortunately, we will not explain this in further detail here): \\
$ PRED = \lambda n.\lambda f.\lambda x.(((n \ (\lambda g.\lambda h.h \ (g \ f))) \ (\lambda u.x)) \ (\lambda v.v)) $ \\
\\
\\
**5.** Define substraction under church numerals: $ SUB = \lambda n.\lambda m. \ ... $ (**Hint**: use $ PRED $).
What happens if you try to substract a bigger number from a smaller one? Evaluate $ SUB \ N1 \ N2 $.
**6.** Define $ LEQ $ (less or equal). $ LEQ \ n \ m $ should return **TRUE** if $ n \leq m $ and **FALSE** if $ n > m $.
**7.** Define $ EQ $ (equality). $ EQ \ n \ m $ should return **TRUE** if $ n = m $ and **FALSE** otherwise.
===== Recursion and the Sage Bird =====
In lambda calculus, recursion is achieved using the fixed-point combinator (or **Y** combinator, // "Why" // bird or //Sage bird//). \\
A fixed-point combinator is a **higher-order** function that returns some fixed point of it's argument function (**x** is a fixed pointed for a function **f** if $ f(x) = x $). That means: $ f \ (fix \ f) = fix \ f $ \\
And by repeated application: $ fix \ f = f \ (f \ (... f \ (fix \ f)...)) $ \\
The **Y**-combinator in lambda calculus looks like this: \\
\\
$ FIX = \lambda f.(\lambda x.f \ (x \ x)) (\lambda x.f \ (x \ x)) $
\\
**8.** Using the **Y**-Combinator, define a function that computes the factorial of a number **n**.
**9.** Using the **Y**-Combinator, define a function $ FIB $ that computes the **n**-th fibonacci number.