Do all six problems below. There are three problems on implementing the lambda calculus and three problems on programming with Church numerals. For problems 1-3, modify the [[linterp.sml]] file. Put your answers for the Church numerals problems, 4-6, in a file called [[church.lam]].

A [[use]] statement loads a file into the interpreter as if it had been typed in directly. A [[use]] statement is of the form

[[-> use filename;]]

When a term is entered at the toplevel, any free variables in the term which appear in the environment are substituted with their bindings in the environment, then the term is reduced to normal form (if possible) and the result is printed.

[[-> term;]]

A binding first evaluates the term on the right hand side as above, and then binds the name on the left hand side to the resulting term in the environment. It is an error for the term on the right hand side to contain any free variables which are not bound in the environment. Bindings are of the form:

[[-> name = term;]]

or

[[-> noreduce name = term;]]

If the [[noreduce]] keyword appears, the term on the right-hand side is not normalized. This tactic can be useful for terms that have no normal form, such as

noreduce bot = (\x.x x)(\x.x x); noreduce Y = \f.(\x.f(x x))(\x.f(x x));

A lambda term can be either a variable, a lambda abstraction, an application, or a
parenthesized lambda term. Precedence is as in ML.
A lambda abstraction is written as follows. Note that each lambda abstraction can
abstract over only one variable.

[[\name.term]]

Application of one term to another is written:

[[t1 t2]]

The lambda interpreter is very liberal about names of variables. A name is any string of characters that contains neither whitespace, nor control characters, nor any of the following characters: [[\]] [[(]] [[)]] [[.]] [[=]] [[/]]. Also, the string [[use]] is reserved and is therefore not a name. So for example, the following are all legal:

1 = \f.\x.f x; True = \x.\y.x; one = True 1;@

For these problems, define appropriate types and functions in
[[linterp.sml]]. When you are done, you will have a working lambda
interpreter. Some of the code we give you (`Lhelp.ui` and
`Lhelp.uo`) is object code only, so you will have to build the
interpreter using Moscow ML.
Just typing `make` should do it.

**1. Evaluation---Basics** [15 points]. This problem has two parts:

- Using ML, create a type definition for a type
`term`, which should represent a term in the untyped lambda calculus. Using your representation, define the following functions with the given types:lam : string -> term -> term (* lambda abstraction *) app : term -> term -> term (* application *) var : string -> term (* variable *) cpsLambda : (* observer *) term -> (string -> term -> 'a) -> (term -> term -> 'a) -> (string -> 'a) -> 'a

These functions should obey the following algebraic laws:cpsLambda (lam x e) f g h = f x e cpsLambda (app e e') f g h = g e e' cpsLambda (var x) f g h = h x

- Using
`cpsLambda`, define a function`toString`of type`term -> string`that converts a term to a string in uScheme syntax. Your`toString`function should be*independent*of your representation. That is, it should work using the functions above.

Our solution to this problem is under 30 lines of ML code.

**2. Evaluation---Substitution** [11 points].
Implement substitution on your `term` representation.
Use a function `subst` of type `string * term -> term -> term`.
To compute the substitution *M[x|–>N]*, you should call
`subst (x, N) M`.

Also define a function [[freeVars]] of type `term -> string list` which returns
a list of all the free variables in a term.

Our solution to this problem is under 40 lines of ML code.

**3. Evaluation---Reductions** [24 points].
In this problem, you use your substitution function to implement two
different evaluation strategies.

- Implement normal-order reduction on terms.
That is, write a function
`reduceN : term -> term`that takes a term, performs a single reduction step (either beta or eta) in normal order, and returns a new term. If the term you are given is already in normal form, your code should raise the exception`NormalForm`, which you should define. - Implement applicative-order reduction on terms.
That is, write a function
`reduceA : term -> term`that takes a term, performs a single reduction step (either beta or eta) in applicative order, and returns a new term. If the term you are given is already in normal form, your code should raise the exception`NormalForm`, which you should reuse from the previous part.

Our solution to this problem is under 20 lines of ML code.

**4. Church Numerals---predecessor** [15 points].
Implement the predecessor function for the Church numerals.
That is, a function [[pred]] such that [[pred(succ n)=n]], and [[pred(0)=0]].
Ultimately, you will write your function in lambda notation acceptable to the
lambda interpreter, but you may find it useful to try to write your
initial version in Typed uScheme (or ML or uScheme) to make it easier to debug.
**To get full credit, you must explain the reasoning behind your answer**.

Remember,
<

*Hint: Define a function [[lag]], so that [[lag(n,m) = (n+1,n)]].
Now, what is [[lag(lag(lag(lag(0,0))))]]?*.

**5. Church Numerals---equality** [15 points].
Assume you have a predecessor function. Use it to implement an equality
function on Church numerals.
Again, turn in your answer using (untyped) lambda notation,
but feel free to start off in uScheme or Typed uScheme if that makes
it easier to debug.
(Don't expect to be able to use ML here; the
Hindley-Milner type system is not powerful enough to express the types
of all of the functions you
need to write.)
**
Do not use recursion or the Y-combinator in your solution.**

Remember the definitions of the Booleans:
<<*>>=
true = \x.\y.x;
false = \x.\y.y;
@
*Hints: Write a function to test if a Church numeral is zero.
Consider that n = m if and only if n <= m and m <= n.
Remember to use the predecessor function.*

**6. Church Numerals---division and modulus** [20 points].
Write a function [[divmod]] such that given two Church numerals [[m]]
and [[n]], [[divmod m n]] returns a lambda-term representing the pair
(m div n, m mod n).
Again, use pure untyped lambda notation.
You may use [[pair]], [[fst]], and [[snd]] as defined in class, and
you will use the functions from the previous problems.
You may not use explicit recursion;
if you want a recursive solution, use the Y combinator.

This function should be doable in about 10 lines of ML.

**Extra Credit---Normal forms galore**.
Discover what Head Normal Form and Weak Head
Normal Form are and
implement reduction strategies for them.
Explain, in an organized way, the differences between the four
reduction strategies you have implemented.

**Extra Credit---Typed Equality**.
For extra credit, write down equality on Church numerals using Typed uScheme,
give the type of the term in algebraic notation,
and explain why this function can't be written in ML.
(By using the ``erasure'' theorem in reverse, you can take your
untyped version and just add type abstractions and type applications.)

Submit using `submit-lambda` from nice.