====== 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.