This is an old revision of the document!


Lab 7. Lambda Calculus. Intro to Haskell

Lambda Calculus represent a axiomatic system that can be used for very basic proofs.
Given a set of variables VARS, a expression under lambda calculus can be:

variable $ x $ $ x \in VARS $
function $ \lambda x.e $ $ x \in VARS $, $ e $ is a $ \lambda $-expression
application $ (e_1 \ e_2) $ $ e_1, e_2 $ are $ \lambda $-expressions

To evaluate $\lambda$-expressions, there are two types of reduction operations:

  • $\alpha$-conversion: given a expression: $ \lambda x.e $, you can rename all occurences of x in e with y (used for avoiding name collisions).
  • $\beta$-reduction: given a expression: $ \lambda x.body \ param$, you can replace all occurences of x in body with param (We will denote this action with: $ body[x \ / \ param] $).
Be careful about the difference between $ E_1 = \lambda x.e_1 \ e_2 $ and $ E_2 = e_1[x \ / \ e_2] $. The former denotes a expression made from a application between a function and a expression , while the latter is the expression obtained applying $ \beta $-reduction to the former. We say $ E_1 $ is reducible to $ E_2 $ ($ E_1 \Rightarrow E_2 $).
We say two expressions are equal, if it is possible to get one of them from the other using only $\alpha$-conversion.

If a expression cannot be reduced further using $ \beta $-reductions, we say the expression is in $ \beta $-normal form.

Free and bound variables

Take the following Scala snippet as an example:

def f(x: Int) = x + y

We can say that the second occurence of $ x $ is bounded by the $ x $ that appears as a function parameter. When we call the function, the occurence of $ x $ is replaced by the argument that was provided to $ f $. In contrast, $ y $ is a free variable.
This code might look weird, where does $ y $ come from? What does it do? Why would we use a variable that we don't instantiate (i.e. is not bound to anything)? Well, the snippet actually comes from a broader context:

def g(x: Int, y: Int) = {
  def f(x: Int) = x + y
  f(x * y)
}

In this new snippet we can see that all variables are bounded, and the free variable from before is bounded by the outer function, but only the free variable, notice that $ x $ is still bounded by the inner function, and the $ x $ parameter of $ g $ is ignored inside $ f $.
The importance of free variables is that only free occurences of a sub-expression can be bounded by the outer expression.

Translating to lambda calculus, when reducing $ \lambda x.e_1 \ e_2 $ to $ e_1[x \ / \ e_2] $, only free occurences of $ x $ in $ e_1 $ will be replaced by $ e_2 $.

More generally, we say that:

  • if all occurences of a variable in a expressions are bounded , the variable is said to be bounded
  • if one occurence of a variable in a expressions is free , the variable is said to be free


Exercise

7.1.1. For every variable occurence, mention if it's a free or a bounded occurence:

  1. $ \lambda y.(\lambda x.x \ (x \ y)) $
  2. $ \lambda x.(x \ \lambda y.(x \ y \ z)) \ (x \ \lambda y.x) $
  3. $ \lambda f.((\lambda x.(f \ (x \ x))) \ (\lambda x.(f \ (x \ x)))) $

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] $:

$ e_1 $ $ e_1[x \ / \ e_2] $ condition
$ x $ $ e_2 $
$ y $ $ y $ $ x \neq y $
$ E_1 \ E_2 $ $ E_1[x \ / \ e_2] \ E_2[x \ / \ e_2] $
$ \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 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 $ )

Evaluation order

Q: If we have multiple redexes in a expression, which one do we evaluate?

A: We can evaluate any of them, and it is guaranteed by Church-Rosser theorem that if the expression is reducible, we will eventually get the same $ \beta $-normal form.

To not just randomly choose redexes, there exist reduction strategies , from which we will use the Normal Order and Applicative Order:

  • Normal Order evaluation consist of always reducing the leftmost, outermost redex (whenever possible, subsitute the arguments into the function body)
  • Applicative Order evaluation consist of always reducing the leftmost, innermost redex (always reduce the function argument before the function itself)
A expression of the form $ \lambda x.e_1 \ e_2 $ is also called a redex (reducible expression)

Exercise

7.1.2. Evaluate in both Normal Order and Applicative Order the following expressions:

  1. $ \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)$
  2. $ \lambda x.y \ (\lambda x.(x \ x) \ \lambda x.(x \ x))$

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

Click to display ⇲

Click to hide ⇱

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

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

7.1.1 Define multiplication under church numerals: $ MULT = \lambda n.\lambda m. \ \ldots $ (without using the Y-combinator)

7.1.2 Define exponentiation under church numerals: $ EXP = \lambda n.\lambda m. \ \ldots $

7.1.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 $.

Click to display ⇲

Click to hide ⇱

$ PRED = \lambda n.\lambda f.\lambda x.(((n \ (\lambda g.\lambda h.h \ (g \ f))) \ (\lambda u.x)) \ (\lambda v.v)) $

$ \phi = \lambda p.PAIR \ (SND \ p) \ (SUCC \ (SND \ p)) $
$ PRED = \lambda.n.FST \ (n \ \phi \ (PAIR \ N0 \ N0)) $


7.1.4 Define substraction under church numerals: $ SUB = \lambda n.\lambda m. \ \ldots $ (Hint: use $ PRED $).

What happens if you try to substract a bigger number from a smaller one? Evaluate $ SUB \ N1 \ N2 $.

7.1.5 Define $ LEQ $ (less or equal). $ LEQ \ n \ m $ should return TRUE if $ n \leq m $ and FALSE if $ n > m $.

7.1.6 Define $ EQ $ (equality). $ EQ \ n \ m $ should return TRUE if $ n = m $ and FALSE otherwise.

In lambda calculus, recursion is achieved using the fixed-point combinator (or Y combinator).
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 \ (\ldots f \ (fix \ f)\ldots)) $
The Y-combinator in lambda calculus looks like this:

$ FIX = \lambda f.(\lambda x.f \ (x \ x)) (\lambda x.f \ (x \ x)) $

7.2.1 Using the Y-Combinator, define a function that computes the factorial of a number n.

7.2.2 Using the Y-Combinator, define a function $ FIB $ that computes the n-th fibonacci number.