Lecture 6 (2015-09-21)
Stack machines; the lambda calculus
**Stack machines**
|Command|Input stack||Output stack|
|:-|-:|:-:|:-|
|IPush n|s|-->|n:s|
|IPlus|n1:n2:s|-->|n1 + n2:s|
|ITimes|n1:n2:s|-->|n1 * n2:s|
|INeg|n:s|-->|(0-n):s|
**The lambda calculus**
The lambda calculus is a notation for *anonymous* functions. We
typically write math functions with names, like: f(x) = x + 1. In the
lambda calculus, we write functions anonymously. For example, f is
equivalent to λx. x + 1.
We introduced an *equational* semantics with two axioms.
The first, *alpha equivalence*, says that we can rename variables so
long as we do it consistently:
(λx. e) =α (λy. e[y/x])
The second, *beta equivalence*, says how to apply functions:
(λx. e1) e2 = e1[e2/x]
A variable is *free* when it's not under λ that binds it. In
λx. x y, the variable y is free but x is *bound*.
We defined substitution as follows:
||||
|-:|:-:|:-|
|x[e/x] |=| e|
|y[e/x] |=| y|
|(e1 e2)[e/x] |=| e1[e/x] e2[e/x]|
|(λx. e')[e/x] |=| λx. e'|
|(λy. e')[e/x] |=| λy. e'[e/x]|
Note that the definition above only *really* makes sense when e---the
term being substituted for x---doesn't have free variables. When e has
free variables, we need to worry about *capture*, where a free
variable in e becomes bound. The standard way of defining
capture-avoiding substitution is to imagine that we have some infinite
source of *fresh* variables, which don't appear in any given term
we're considering. We then change the last line to read:
(λy. e')[e/x] = λz. e'[z/y][e/x] where z is fresh
The freshness of z makes sure that neither e' nor e mention z at all.
**Church encodings**
We also defined a bunch of *Church encodings*, named after Alonzo Church,
the progenitor and master encoder of the lambda calculus.
We first defined the Church booleans:
true = λx. &lambda y. x
false = λx. &lambda y. x
Then, to check our work, we defined a conditional:
cond = λp. λt. λe. p t e
We can then see that:
cond true e1 e2 = e1
and:
cond false e1 e2 = e2
That is... it really works! The idea here is to *operationalize* every
thing. The booleans are typicaly defined as **2** = { ⊥, ⊤
}, but we instead say that the *Church booleans* are the functions that make a
choice between two things.
We can define numbers, too. A *Church numeral* is a function that
takes two arguments and applies the first one some number of times.
So 0 is the function that never applies its first argument:
zero = λs. λz. z
And 1 applies it once:
one = λs. λz. s z
And two applies it twice:
two = λs. λz. s (s z)
We can write the *successor function*, which takes a number and then
yields its successor.
succ = λn. λs. λz. s (n s z)
**Types**
Naive Church encodings don't always work in Haskell due to typing, but
we can *think* of each of these Church encodings as having a type.
> type ChurchBoolean = a -> a -> a
> type ChurchNumeral = (a -> a) -> a -> a
**Shorthand notation**
When a function takes a bunch of arguments---like succ above---we
sometimes write a single lambda, like so:
succ = λn s z. s (n s z)