Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:2024:l07 [2024/04/09 22:08]
tpruteanu
pp:2024:l07 [2024/05/23 12:09] (current)
tpruteanu [7.4. Natural Numbers - Church numerals]
Line 3: Line 3:
 ===== 7.0. What? Why? ===== ===== 7.0. What? Why? =====
  
-**Lambda Calculus** is a universal model of computation (can be used to simulate any Turing Machine) based on function //​abstraction//​ and //​application//​. It has a very simple semantic that can be used to study properties of computation. \\+**Lambda Calculus** is a universal model of computation (can be used to simulate any Turing Machine) based on function //​abstraction//​ and //​application//​. It has a very simple semantic that can be used to study properties of comput---- 
 +ation. \\
  
 The first thing to take note of is that **EVERYTHING** is a function (a algorithm, the input and the output are all functions). \\ The first thing to take note of is that **EVERYTHING** is a function (a algorithm, the input and the output are all functions). \\
Line 77: Line 78:
 **7.1.1. ** For every variable occurence, mention if it's a // free // or a // bounded // occurence: **7.1.1. ** For every variable occurence, mention if it's a // free // or a // bounded // occurence:
   - $ \lambda y.(\lambda x.x \ (x \ y)) $   - $ \lambda y.(\lambda x.x \ (x \ y)) $
-  - $ \lambda x.(x \ \lambda y.((x \ y) \ z)) \ (x \ \lambda y.x) $+  - $ (\lambda x.(x \ \lambda y.((x \ y) \ z)) \ (x \ \lambda y.x)) $
   - $ \lambda f.(\lambda x.f \ (x \ x)) \ (\lambda x.f \ (x \ x)) $   - $ \lambda f.(\lambda x.f \ (x \ x)) \ (\lambda x.f \ (x \ x)) $
  
-==== Reduction rules ====+===== 7.2. Reduction rules =====
  
 Using what we learned from // free // and // bounded // variables, we can define a algorithm for $\beta$**-reduction**,​ given a expression $ e_1[x \ / \ e_2] $: Using what we learned from // free // and // bounded // variables, we can define a algorithm for $\beta$**-reduction**,​ given a expression $ e_1[x \ / \ e_2] $:
Line 89: Line 90:
 | $ \lambda x.e $ | $ \lambda x.e $ | | | $ \lambda x.e $ | $ \lambda x.e $ | |
 | $ \lambda y.e $ | $ \lambda y.e[x \ / \ e_2] $ | $ x \neq y $, $ y $ does not appear // free // in $ e_2 $| | $ \lambda y.e $ | $ \lambda y.e[x \ / \ e_2] $ | $ x \neq y $, $ y $ does not appear // free // in $ e_2 $|
-| $ \lambda y.e $ | $ \{\lambda z.e[y \ / \ z]\}[x \ / \ e_2] $ | $ x \neq y $, appears // free // in $ e_2 $| ( $ z $ is a new variable that is not free in $ e $ or $ e_2 $ ) |+| $ \lambda y.e $ | $ \{\lambda z.e[y \ / \ z]\}[x \ / \ e_2] $ | $ x \neq y $, $ y $ appears // free // in $ e_2 $| ( $ z $ is a new variable that is not free in $ e $ or $ e_2 $ ) |
  
 ==== Evaluation order ==== ==== Evaluation order ====
Line 102: Line 103:
  
 <note important>​ <note important>​
-A expression of the form $ \lambda x.e_1 \ e_2 $ is also called a **redex** (reducible expression)+A expression of the form $(\lambda x.e_1 \ e_2)$ is also called a **redex** (reducible expression)
 </​note>​ </​note>​
  
 **Exercise** \\ **Exercise** \\
  
-**7.1.2. ** Evaluate in both **Normal Order** and **Applicative Order** the following expressions:​ +**7.2.1. ** Evaluate in both **Normal Order** and **Applicative Order** the following expressions:​ 
-  - $ \lambda x.\lambda y.(x \ y \ x) \ \lambda x.\lambda y.x \ (\lambda x.\lambda y.\lambda z.(x \ z \ y) \ \lambda x.\lambda y.y)$ +  - $ (\lambda x.\lambda y.\lambda z.((x \ z) \ y) \ \lambda x.\lambda y.x) $ 
-  - $ \lambda x.y \ (\lambda x.(x \ x) \ \lambda x.(x \ x))$+  - $ ((\lambda x.\lambda y.((x \ y) \ x) \ \lambda x.\lambda y.x\ (\lambda x.\lambda y.\lambda z.((x \ z\ y) \ \lambda x.\lambda y.y))$ 
 +  - $ (\lambda x.y \ (\lambda x.(x \ x) \ \lambda x.(x \ x)))$
  
-==== Lambda calculus as a programming language ​(optional) ​====+===== Lambda calculus as a programming language ​=====
  
 The [[https://​en.wikipedia.org/​wiki/​Church%E2%80%93Turing_thesis | Church-Turing thesis]] asserts that any //​computable//​ function can be computed using lambda calculus (or Turing Machines or equivalent models). \\ The [[https://​en.wikipedia.org/​wiki/​Church%E2%80%93Turing_thesis | Church-Turing thesis]] asserts that any //​computable//​ function can be computed using lambda calculus (or Turing Machines or equivalent models). \\
-For the curious, a series of additional exercises covering this topic can be found here: [[pp:​2023:​haskell:​l07-extra|Lambda Calculus as a programming language]]. \\ 
  
 +How can this be? Everything in Lambda Calculus is a function, there are no numbers to compute //stuff// with. Well, while there are not the numbers we are used to, we can define **higher-order functions** that are analogs for concepts we are familiar with and use them instead. \\
 +
 +The representations we are going to present further are also called **Church encodings**,​ because they were first used by Alonzo Church, the inventor of Lambda Calculus.
 +
 +==== 7.3. 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.x$ \\
 +$ FALSE = \lambda x.\lambda y.y$ \\
 +
 +<​note>​
 +As we defined it, **TRUE** is sometimes called the **K**-Combinator (or //​Kestrel//​),​ and **FALSE** the **KI**-Combinator (or //Kite//). \\
 +{{:​pp:​2024:​kestrel.jpg?​nolink&​200|}}
 +{{:​pp:​2024:​kite.jpg?​nolink&​200|}}
 +</​note>​
 +
 +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) $ \\
 +
 +<​hidden>​
 +<​note> ​
 +**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//​). \\
 +{{:​pp:​2024:​cardinal.jpg?​nolink&​200|}}
 +</​note>​
 +</​hidden>​
 +
 +----
 +
 +**Exercises** \\
 +**7.3.1.** Define the $ XOR $ operations over booleans.
 +
 +**7.3.2.** Define the $ NAND $ operations over booleans.
 +
 +**7.3.3.** Define the $ NOR $ operations over booleans.
 +
 +==== Pairs - Lecture Reminder ====
 +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 a.\lambda b.\lambda z.((z \ a )\ b) $ \\
 +$ FIRST = \lambda p.(p \ TRUE) $ \\
 +$ SECOND = \lambda p.(p \ FALSE) $ \\
 +
 +<​note>​
 +The $ PAIR $ higher-order function we defined is also called the **V**-Combinator (or //Vireo//). \\
 +{{:​pp:​2024:​vireo.jpg?​nolink&​200|}}
 +</​note>​
 +
 +==== 7.4. Natural Numbers - Church numerals ====
 +
 +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)) $ \\
 +...
 +
 +<​note>​ Does **N0** look familiar? It's the same as **FALSE** if you rename the variables (using $\alpha$-reduction). </​note>​
 +
 +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)) $ \\
 +\\
 +
 +----
 +**Exercises** \\
 +
 +**7.4.1.** Define multiplication under church numerals: $ MULT = \lambda n.\lambda m. \ ... $ (**Hint:** you can do it without the **Y**-Combinator)
 +
 +**7.4.2.** Define exponentiation under church numerals: $ EXP = \lambda n.\lambda m. \ ... $
 +
 +**7.4.3. (*)** 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:​** \\
 +<​hidden>​
 +Let's start with defining a // 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 iterated multiple times (on itself), we make the input another pair, where the second value is the '​real'​ input: \\
 +$ \phi = \lambda p.((PAIR \ (SECOND \ p)) \ (SUCC \ (SECOND \ p))) $ \\
 +\\
 +This takes a pair ( $ n $, $ (SUCC \ n) $) and returns another pair ($ (SUCC \ n) $, $ (SUCC \ (SUCC \ n)) $
 +\\
 +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) $ \\
 +\\
 +</​hidden>​
 +
 +\\
 +
 +**7.4.4.** 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 )$.
 +
 +**7.4.5.** Define $ LEQ $ (less or equal). $ LEQ \ n \ m $ should return **TRUE** if $ n \leq m $ and **FALSE** if $ n > m $.
 +
 +**7.4.6.** Define $ EQ $ (equality). $ EQ \ n \ m $ should return **TRUE** if $ n = m $ and **FALSE** otherwise.
 +
 +==== 7.5. 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)) $
 +
 +----
 +**Exercises** \\
 +
 +**7.5.1. (*)** Using the **Y**-Combinator,​ define a function that computes the factorial of a number **n**.
 +
 +**7.5.2. (*)** Using the **Y**-Combinator,​ define a function $ FIB $ that computes the **n**-th fibonacci number.