CS 334

Show that the ML types S > (T > U) and (S * T) > U are essentially "the same" by defining higherorder ML functions
Curry: ((S * T) > U) > (S> (T > U)) and UnCurry: (S > (T > U))> ((S * T) > U)such that for all f : (S * T) > U and g: S > (T > U), (i) UnCurry (Curry (f)) = f, and (ii) Curry (UnCurry (g)) = g. This shows a onetoone correspondence between the two types.
That is, you must write ML functions Curry and UnCurry with types as above and then "prove" that the two equations above always hold for your functions and any f and g of the appropriate types. The "proof" is most easily done by applying both the lefthand and righthand sides of the equation to a general term of the appropriate type (e.g., for (i) apply it to a pair (s,t): S*T) and showing that they both give the same answer. Thus, to prove (i) show UnCurry(Curry (f)) (s,t) = f (s,t) by expanding the definitions of UnCurry and Curry that you are providing. (Note that you can't compute with types  function bodies are required!)
let x = 1 in let g = fn y => succ x in let x = 7 in g 0 end end endRecall that the parser translates (let x = M in N end) into ((fn x => N) M).
b. What is the correct answer (independent of your interpreter) for the case in which static scoping is desired.
c. What is the correct answer for dynamic scoping?
Define the datatypes
datatype value = NUM of int  BOOL of bool  SUCC  PRED  ISZERO  CLOSURE of (string * term * env)  THUNK of term * env  ERROR withtype env = (string * value) list;
where env represents the type of environments. The withtype construct allows one to define a datatype with a mutually recursive type definition. Environments are one way of encoding substitutionthe bindings of free variables in a PCF term are given by an environment. The type value represents the final answers, or values, returned by PCF programs. The first five are selfexplanatory. The sixth value, a closure, is the representation for functions: the first part of a closure is the formal parameter, the second part is the body of the function, and the third part is an environment that gives meaning to the free variables in the body of the function. A THUNK is a way of suspending evaluation of a term (similar to a closure), which involves saving the term and its current environment.
a. An environment is represented as a list of pairs of the form (id,val) where id is the name of the identifier and val is its value in the environment. Thus,
[("x",NUM 12),("flag",BOOL true)}represents an environment where identifier x has value NUM 12 and flag has value BOOL true.
Define a function, getVal id ev, that, given a string, id representing a variable, and an environment, ev, returns the value associated with id in ev, if there is one. Otherwise it should return ERROR.
Also define a function update ev id newVal, that takes an environment, ev, an identifier, id, and value, newVal, and returns an updated environment which is identical to ev except that id is associated with newVal. The easiest way to construct this new environment is simply to add the new pair to the beginning of the list corresponding to ev. If getVal always returns the first binding for an identifier, then old bindings do not need to be removed when new ones are added. We write this update operation in our rules below as ev[id := newVal].
Discussion: In the interpreter defined in part c, applying a function to an argument will result in an updated environment (the formal parameter will be associated with the value of the actual parameter in the updated environment). The function body will then be evaluated in this new environment. The only question is, which environment should be updated to reflect the actual parameter? Since we are considering statically scoped languages here, the environment to be updated is the one in effect when the function was defined, rather than the one in place when the function is called! Since we don't have many names floating around here it may be difficult for you to appreciate this distinction. However, look at the following example:
let f = fn x => (iszero (succ x)) in let g = fn y => f y in let f = fn x => (iszero x) in g 0
where (let x = M in N) is shorthand for ((fn x => N) M). This should evaluate to false under static scoping (the f in the definition of g is the outermost f), while it returns true under dynamic scoping (the f in the definition of g refers to the most recently defined value of f  the innermost one). Make sure your interpreter evaluates this expression (after you have gotten rid of all of the let's) properly.
b. Define a function, newinterp t ev, that takes a term, t, (possibly involving free variables) and an environment, ev, that gives meaning to all the free variables in the term, and returns a value representing the evaluation of the term in that environment. (Note: In the initial call to evaluate a term, you will want to pass your default environment from above. Nevertheless, the function should still take two arguments, since in recursive callsparticularly with applying functionsyou will want to call newinterp with different environments. See rule 11 below.) In the rules below, ev stands for an environment:
(1) (n, ev) => n for n an integer. (2) (true, ev ) => true, and similarly for false (3a) (x, ev) => ev(x), if ev(x) is not a thunk (3b) (x, ev) => v, if ev(x) is of the form thunk(e,ev') and (e, ev') => v, otherwise (4) (error, ev) => error (5) (succ, ev) => succ, and similarly for the other initial functions (6) (fn x => e, ev) => closure(x, e, ev)Notice that in closures we save the defining environment along with the formal parameter and function body.
(b, ev) => true, (e1, ev) => v (7a)  (if b then e1 else e2, ev) => v (b, ev) => false, (e2, ev) => v (7b)  (if b then e1 else e2, ev) => v (e1, ev) => succ, (e2, ev) => n (8)  ((e1 e2), ev) => (n+1) (e1, ev) => pred, (e2, ev) => 0 (9a)  ((e1 e2), ev) => 0 (e1, ev) => pred, (e2, ev) => (n+1) (9b)  ((e1 e2), ev) => n (e1, ev) => iszero, (e2, ev) => 0 (10a)  ((e1 e2), ev) => true (e1,ev) => iszero, (e2, ev) => (n+1) (10b)  ((e1 e2), ev) => false (e1, ev) => closure(x, e3, ev_{f}), (e2, ev) => v1, (e3, ev_{f}[x:=v1]) => v (11)  ((e1 e2), ev) => vNotice that the body of the function is interpreted in the environment from the closure, updated to reflect the assignment of the actual parameter value to the formal.
(e, ev[x:=thunk(rec x => e, ev)]) => v (12)  (rec x => e, ev) => vNotice that in evaluating a recursive expression we simply evaluate the body in an environment in which the recursive name stands for the recursive expression. It is important to put this in the environment so that other recursive calls can be evaluated properly. We use thunks to suspend the evaluation of the call so that it will be not be evaluated until it is needed. When the thunk is encountered, the term can be evaluated in the environment stored with it (as in rule 3).