From e171fd1236da1479e08f7753a0802c4cd6b5efa8 Mon Sep 17 00:00:00 2001
From: Chris
Date: Thu, 5 Feb 2015 14:05:55 0500
Subject: [PATCH] notes on lambda calculus

topics/_week2_lambda_calculus_fine_points.mdwn  63 +++++++
topics/_week2_lambda_calculus_intro.mdwn  248 ++++++++++++++++++++
2 files changed, 264 insertions(+), 47 deletions()
create mode 100644 topics/_week2_lambda_calculus_fine_points.mdwn
diff git a/topics/_week2_lambda_calculus_fine_points.mdwn b/topics/_week2_lambda_calculus_fine_points.mdwn
new file mode 100644
index 00000000..2176f359
 /dev/null
+++ b/topics/_week2_lambda_calculus_fine_points.mdwn
@@ 0,0 +1,63 @@
+Fine points concerning the lambda calculus
+==========================================
+
+Hankin uses the symbol
+→
for onestep contraction,
+and the symbol ↠
for
+zeroormore step reduction. Hindley and Seldin use
+⊳_{1}
and
+⊳
.
+
+When M and N are such that there's some P that M reduces to by zero or
+more steps, and that N also reduces to by zero or more steps, then we
+say that M and N are **betaconvertible**. We'll write that like this:
+
+ M <~~> N
+
+This is what plays the role of equality in the lambda calculus. Hankin
+uses the symbol `=` for this. So too do Hindley and
+Seldin. Personally, I keep confusing that with the relation to be
+described next, so let's use this notation instead. Note that `M <~~>
+N` doesn't mean that each of `M` and `N` are reducible to each other;
+that only holds when `M` and `N` are the same expression. (Or, with
+our convention of only saying "reducible" for one or more reduction
+steps, it never holds.)
+
+In the metatheory, it's also sometimes useful to talk about formulas
+that are syntactically equivalent *before any reductions take
+place*. Hankin uses the symbol ≡
for this. So too
+do Hindley and Seldin. We'll use that too, and will avoid using `=`
+when discussing the metatheory. Instead we'll use `<~~>` as we said
+above. When we want to introduce a stipulative definition, we'll write
+it out longhand, as in:
+
+> T is defined to be `(M N)`.
+
+We'll regard the following two expressions:
+
+ (\x (x y))
+
+ (\z (z y))
+
+as syntactically equivalent, since they only involve a typographic
+change of a bound variable. Read Hankin section 2.3 for discussion of
+different attitudes one can take about this.
+
+Note that neither of those expressions are identical to:
+
+ (\x (x w))
+
+because here it's a free variable that's been changed. Nor are they identical to:
+
+ (\y (y y))
+
+because here the second occurrence of `y` is no longer free.
+
+There is plenty of discussion of this, and the fine points of how
+substitution works, in Hankin and in various of the tutorials we've
+linked to about the lambda calculus. We expect you have a good
+intuitive understanding of what to do already, though, even if you're
+not able to articulate it rigorously.
+
+* [More discussion in week 2 notes](/week2/#index1h1)
+
diff git a/topics/_week2_lambda_calculus_intro.mdwn b/topics/_week2_lambda_calculus_intro.mdwn
index 7c1e4d0e..68beb1c7 100644
 a/topics/_week2_lambda_calculus_intro.mdwn
+++ b/topics/_week2_lambda_calculus_intro.mdwn
@@ 28,14 +28,28 @@ Each variable is an expression. For any expressions M and N and variable a, the
Abstract: (λa M)
We'll tend to write (λa M)
as just `(\a M)`, so we don't have to write out the markup code for the λ
. You can yourself write (λa M)
or `(\a M)` or `(lambda a M)`.
+We'll tend to write (λa M)
as just `(\a M)`, so we
+don't have to write out the markup code for the
+λ
. You can yourself write (λa
+M)
or `(\a M)` or `(lambda a M)`.
Application: (M N)
+Expressions in the lambda calculus are called "terms". Here is the
+syntax of the lambda calculus given in the form of a contextfree
+grammar:
Examples of expressions:
+ T > ( T T )
+ T > ( lambda Var T)
+ T > x
+ T > y
+ ...
+
+Very, very simple.
+
+Examples of terms:
x
(y x)
@@ 52,13 +66,17 @@ proof theory as having just one rule, called the rule of **betareduction** or
((\a M) N)
that is, an application of an abstract to some other expression. This compound form is called a **redex**, meaning it's a "betareducible expression." `(\a M)` is called the **head** of the redex; `N` is called the **argument**, and `M` is called the **body**.
+This is an application whose first element is an abstract. This
+compound form is called a **redex**, meaning it's a "betareducible
+expression." `(\a M)` is called the **head** of the redex; `N` is
+called the **argument**, and `M` is called the **body**.
The rule of betareduction permits a transition from that expression to the following:
 M [a:=N]
+ M [a < N]
What this means is just `M`, with any *free occurrences* inside `M` of the variable `a` replaced with the term `N`.
+What this means is just `M`, with any *free occurrences* inside `M` of
+the variable `a` replaced with the term `N`.
What is a free occurrence?
@@ 70,10 +88,16 @@ What is a free occurrence?
For instance:

> T is defined to be `(x (\x (\y (x (y z)))))`
The first occurrence of `x` in T is free. The `\x` we won't regard as containing an occurrence of `x`. The next occurrence of `x` occurs within a form that begins with `\x`, so it is bound as well. The occurrence of `y` is bound; and the occurrence of `z` is free.
+The first occurrence of `x` in T is free. The `\x` we won't regard as
+containing an occurrence of `x`. The next occurrence of `x` occurs
+within a form that begins with `\x`, so it is bound as well. The
+occurrence of `y` is bound; and the occurrence of `z` is free.
+
+Note that the definition of *bound* is carefully crafted to guarantee
+that in an expression like `(\a (x a))`, both occurrences of `a` count
+as bound.
To read further:
@@ 91,49 +115,32 @@ We'll write that like this:
((\x (y x)) z) ~~> (y z)
Different authors use different notations. Some authors use the term "contraction" for a single reduction step, and reserve the term "reduction" for the reflexive transitive closure of that, that is, for zero or more reduction steps. Informally, it seems easiest to us to say "reduction" for one or more reduction steps. So when we write:
+Different authors use different notations. Some authors use the term
+"contraction" for a single reduction step, and reserve the term
+"reduction" for the reflexive transitive closure of that, that is, for
+zero or more reduction steps. Informally, it seems easiest to us to
+say "reduction" for one or more reduction steps. So when we write:
M ~~> N
We'll mean that you can get from M to N by one or more reduction steps. Hankin uses the symbol →
for onestep contraction, and the symbol ↠
for zeroormore step reduction. Hindley and Seldin use ⊳_{1}
and ⊳
.

When M and N are such that there's some P that M reduces to by zero or more steps, and that N also reduces to by zero or more steps, then we say that M and N are **betaconvertible**. We'll write that like this:

 M <~~> N

This is what plays the role of equality in the lambda calculus. Hankin uses the symbol `=` for this. So too do Hindley and Seldin. Personally, I keep confusing that with the relation to be described next, so let's use this notation instead. Note that `M <~~> N` doesn't mean that each of `M` and `N` are reducible to each other; that only holds when `M` and `N` are the same expression. (Or, with our convention of only saying "reducible" for one or more reduction steps, it never holds.)

In the metatheory, it's also sometimes useful to talk about formulas that are syntactically equivalent *before any reductions take place*. Hankin uses the symbol ≡
for this. So too do Hindley and Seldin. We'll use that too, and will avoid using `=` when discussing the metatheory. Instead we'll use `<~~>` as we said above. When we want to introduce a stipulative definition, we'll write it out longhand, as in:

> T is defined to be `(M N)`.

We'll regard the following two expressions:
+We'll mean that you can get from M to N by one or more reduction
+steps.
 (\x (x y))

 (\z (z y))

as syntactically equivalent, since they only involve a typographic change of a bound variable. Read Hankin section 2.3 for discussion of different attitudes one can take about this.

Note that neither of those expressions are identical to:

 (\x (x w))
+There are many more details about the notation and the metatheory of
+the lambda calculus here:
because here it's a free variable that's been changed. Nor are they identical to:

 (\y (y y))

because here the second occurrence of `y` is no longer free.

There is plenty of discussion of this, and the fine points of how substitution works, in Hankin and in various of the tutorials we've linked to about the lambda calculus. We expect you have a good intuitive understanding of what to do already, though, even if you're not able to articulate it rigorously.

* [More discussion in week 2 notes](/week2/#index1h1)
+* [[topics/_week2_lambda_calculus_fine_points.mdwn]]
Shorthand

The grammar we gave for the lambda calculus leads to some verbosity. There are several informal conventions in widespread use, which enable the language to be written more compactly. (If you like, you could instead articulate a formal grammar which incorporates these additional conventions. Instead of showing it to you, we'll leave it as an exercise for those so inclined.)
+The grammar we gave for the lambda calculus leads to some
+verbosity. There are several informal conventions in widespread use,
+which enable the language to be written more compactly. (If you like,
+you could instead articulate a formal grammar which incorporates these
+additional conventions. Instead of showing it to you, we'll leave it
+as an exercise for those so inclined.)
**Parentheses** Outermost parentheses around applications can be dropped. Moreover, applications will associate to the left, so `M N P` will be understood as `((M N) P)`. Finally, you can drop parentheses around abstracts, but not when they're part of an application. So you can abbreviate:
@@ 196,7 +203,27 @@ Similarly, `(\x (\y (\z M)))` can be abbreviated as:
Lambda terms represent functions

The untyped lambda calculus is Turing complete: all (recursively computable) functions can be represented by lambda terms. For some lambda terms, it is easy to see what function they represent:
+Let's pause and consider the following fundamental question: what is a
+function? One popular answer is that a function can be represented by
+a set of ordered pairs. This set is called the **graph** of the
+funtion. If the ordered pair `(a,b)` is a member of the graph if `f`,
+that means that `f` maps the argument `a` to the value `b`. In
+symbols, `f a == b`.
+
+There is a requirement that in order to count as a function (rather
+than as merely a more general relation), the graph cannot contain two
+(distinct) ordered pairs with the same first element. That is, in
+order to count as a proper function, each argument must correspond to
+a unique result.
+
+The essential usefullness of the lambda calculus is that it is
+wonderfully suited to representing functions. In fact, the untyped
+lambda calculus is Turing Complete [[!wikipedia Turing Completeness]]:
+all (recursively computable) functions can be represented by lambda
+terms. (As we'll see, much of the fun will be in unpacking the word
+"represented".)
+
+For some lambda terms, it is easy to see what function they represent:
> `(\x x)` represents the identity function: given any argument `M`, this function
simply returns `M`: `((\x x) M) ~~> M`.
@@ 204,10 +231,22 @@ simply returns `M`: `((\x x) M) ~~> M`.
> `(\x (x x))` duplicates its argument:
`((\x (x x)) M) ~~> (M M)`
+> `(\x (\y (y x)))` reorders its two arguments:
+`(((\x (\y (y x))) M) N) ~~> (N M)`
+
> `(\x (\y x))` throws away its second argument:
`(((\x (\y x)) M) N) ~~> M`
and so on.
+and so on. In order to get an intuitive feel for the power of the
+lambda calculus, note that duplicating, reordering, and deleting
+elements is all that it takes to simulate the behavior of a general
+word processing program. That means that if we had a big enough
+lambda term, it could take a representation of *Emma* as input and
+produce *Hamlet* as a result.
+
+Some of these functions are so useful that we'll give them special
+names. In particular, we'll call the identity function `(\x.x)`
+**I**, and the function `(\x\y.x)` **K**.
It is easy to see that distinct lambda expressions can represent the same
function, considered as a mapping from input to outputs. Obviously:
@@ 228,17 +267,132 @@ both represent the same function, the identity function. However, we said above
yet when applied to any argument M, all of these will always return M. So they have the same extension. It's also true, though you may not yet be in a position to see, that no other function can differentiate between them when they're supplied as an argument to it. However, these expressions are all syntactically distinct.
The first two expressions are *convertible*: in particular the first reduces to the second. So they can be regarded as prooftheoretically equivalent even though they're not syntactically identical. However, the proof theory we've given so far doesn't permit you to reduce the second expression to the third. So these lambda expressions are nonequivalent.

There's an extension of the prooftheory we've presented so far which does permit this further move. And in that extended proof theory, all computable functions with the same extension do turn out to be equivalent (convertible). However, at that point, we still won't be working with the traditional mathematical notion of a function as a set of ordered pairs. One reason is that the latter but not the former permits many uncomputable functions. A second reason is that the latter but not the former prohibits functions from applying to themselves. We discussed this some at the end of Monday's meeting (and further discussion is best pursued in person).

+The first two expressions are *convertible*: in particular the first
+reduces to the second via a single instance of beta reduction. So they
+can be regarded as prooftheoretically equivalent even though they're
+not syntactically identical. However, the proof theory we've given so
+far doesn't permit you to reduce the second expression to the
+third. So these lambda expressions are nonequivalent.
+
+To use a linguistic analogy, you can think of what we're calling
+"proof theory" as a kind of syntactic transformation. That is, the
+sentences *John saw Mary* and *Mary was seen by John* are not
+syntactically identical, yet (on some theories) one can be derived
+from the other. The key element in the analogy is that this kind of
+syntactic derivation is supposed to preserve meaning, so that the two
+sentence mean (roughly) the same thing.
+
+There's an extension of the prooftheory we've presented so far which
+does permit this further move. And in that extended proof theory, all
+computable functions with the same extension do turn out to be
+equivalent (convertible). However, at that point, we still wouldn't be
+working with the traditional mathematical notion of a function as a
+set of ordered pairs. One reason is that the fully general
+mathematical notion permits many uncomputable functions, but the
+lambda calculus is capable of expressing only computable functions. A
+second reason is that the full mathematical notion prohibits
+functions from applying to themselves, but in the lambda calculus,
+it is permitted for a function to take itself as an argument (for
+instance, there is nothing wrong with applying the identity function
+to itself, thus: `(\x x)(\x x)`. This is a redex that reduces to the
+identity function (of course).
+
+
+The analogy with `let`
+
+
+In our basic functional programming language, we used `let`
+expressions to assign values to variables. For instance,
+
+ let x match 2
+ in (x, x)
+
+evaluates to the ordered pair (2, 2). It may be helpful to think of
+a redex in the lambda calculus as a particular sort of `let`
+construction.
+
+ ((\x M) N) is analogous to
+
+ let x match N
+ in M
+
+This analogy should be treated with caution. For one thing, our
+`letrec` allowed us to define recursive functions in which the name of
+the function appeared within the expression `N`; we're not ready to
+deal with recursive functions in the lambda calculus yet. For
+another, we defined `let` in terms of values, and at this point, the
+lambda calculus doesn't have values, it only has other expressions.
+So it might be better to translate `((\x M) N)` as `let x be replaced
+by N in M`, or some such. Most problematically, in the lambda
+calculus, an abstract such as `(\x (x x))` is perfectly wellformed
+and coherent, but it is not possible to write a `let` expression that
+does not have an argument (here, `N`) explicitly present.
+
+Nevertheless, the correspondence is close enough that it can spur
+intuition.
Booleans and pairs
==================
Our definition of these is reviewed in [[Assignment1]].
+So if the lambda calculus can represent any computable function, we
+need to do some work to show how to represent some of the functions
+we've become acquainted with. We'll start with the `if ... then
+... else...` construction.
+
+ if x then y else z
+
+For a boolean expression `x`, this complex expression evaluates to `y`
+if `x` evaluates to `'true`, and to `z` if `x` evaluations to `'false.
+So in order to simulate and `if` clause in the lambda calculus, we
+need to settle on a way to represent `'true` and `'false`.
+
+We could simply add the constants `'true` and `'false` to the
+language, since it makes sense to add constants to the lambda
+calculus. However, that would also require complicating the
+interpretation of the language; at the very least, we would need more
+than just beta reduction as our engine of computation. In the spirit
+of computational minimalism, let's see how far we can get with the
+pure lambda calculus, without any special constants.
+
+So we'll need to get rid of the parts of the `if` statement that are
+just syntactic windowdressing. That is, we'll need to get rid of the
+`if`, the `then`, and the `else`:
+
+ M N L
+
+Recall that our convention is that values associate left to right, so
+this series of terms is evaluated as
+
+ ((M N) L)
+
+If this expression is supposed to have the meaning of `if M then N
+else L`, then we need to find a value for `'true` such that when it is
+substituted in place of `M`, the expression evaluates to `N`. The
+function we're looking for takes two arguments: first `N`, then `L`,
+throws away `L`, and returns `N`.
+
+We've already seen such a function. We called it **K**: `(\x\y.x)`.
+Let's test:
+
+ ((K M) L) == (((\x\y.x) M) L) ~~> ((\y.M) L) ~~> M
+
+Sucess! In the same spirit, `'false` will be **KI**:
+
+ (((K I) M) L) == ((((\x\y.x)(\x.x)) M) L)
+ ~~> (((\y.(\x.x)) M) L)
+ ~~> ((\x.x) L)
+ ~~> L
+
+So have seen our first major representation in the lambda calculus:
+"true" is represented by **K**, and "false" is represented by **KI**.
+We'll be building up a lot of representations in the weeks to come,
+and they will all maintain the discipline that if a expression is
+meant to be interpreted as a truth value (i.e., as a Boolean), it will
+evaluate to (an expression that is alphabetaeta equivalent to) **K**
+or **KI**.
+Our definition of these is reviewed in [[Assignment 2topics/assignment2]].
It's possible to do the assignment without using a Scheme interpreter, however
you should take this opportunity to [get Scheme installed on your

2.11.0