CS 334
Programming Languages
Spring 2002

Assignment 4 -- Solutions


  1. Prob. 5.8, pg. 147. Soln omitted. The print() statement prints 3, 1 with static scoping and 3, 4 for dynamic scoping.

  2. Define Curry: ((S * T) -> U) -> (S-> (T -> U)) and UnCurry: (S -> (T -> U))-> ((S * T) -> U).

    Solution:

    fun Curry f s t = f(s,t);
           
    fun UnCurry g (s,t) = g s t
    
    or equivalently,
            fun Curry f = fn s => fn t => f(s,t);
    
            fun UnCurry g = fn (s,t) => g s t
    
    For the next part, a proof was required, not an example. Many of you seemed to be willing to believe the following proof that the identity function always returns primes:
    Proof: Id(5) = 5, which is a prime, therefore Id(n) always returns a prime!

    OK, here is a real proof that Curry and UnCurry undo each other:

    (i) show UnCurry (Curry (f)) (s,t) = f (s,t).

        UnCurry (Curry f) (s,t) = (fn (s,t) => (Curry f) s t) (s,t) (by def of UnCurry)
                                = (Curry f) s t 
                                = (fn s => fn t => f(s,t)) s t
                                = f (s,t)
    
    (ii) show Curry (UnCurry (g)) s t = g s t.
        Curry (Uncurry g) s t   = (Uncurry g) (s,t)
                                = g s t
    
  3. Problem 6.4, page 6-49 of Louden. Be sure to explain your answers.

    a. x and y are considered to have the same type since they are declared together, while z has a different type in the name equivalence, because it is declared with a different descriptive type definition.

    b. The best way to fix the code is to give the struct type a name with a typedef and then use the name in all of the declarations. An alternative, not as good, way is to declare them all in the same variable declaration (i.e., erase the second type, and list x,y,z in the first declaration).

  4. Problem 6.22, page 6-52 of Louden. Be sure to explain your answers.

    a. All of a, b, c, and d are type equivalent under structural equivalence,

    b. Only a and b are type equivalent under name equivalence

    c. Variables a, b, and c are type equivalent under the actual C rules.

  5. a. What value does your interpreter return in evaluating:
            let x = 1
            in let g = fn y => succ x
               in let x = 7
                  in g 0 end end end
    Should be 2.

    b. What is the correct answer (independent of your interpreter) for the case in which static scoping is desired. 2

    c. What is the correct answer for dynamic scoping? 8

  6. Interpreter:
    datatype value = NUM of int | BOOL of bool | SUCC | PRED | 
    		       ISZERO | CLOSURE of (string * term * env) | 
    		       THUNK of term * env | ERROR of string * value
    withtype env = (string * value) list;
    (* Note that ERROR terms are used for debugging purposes when an
     error occurs during expression evaluation. *)
    
    fun is_error (ERROR(a,b)) = true
      | is_error other = false;
        
    fun update environ nustr nuval:env = (nustr,nuval)::environ;
    
    fun getVal id [] = ERROR (id^"not in environment",NUM 0)
      | getVal id ((id2,val2)::rest) = if (id = id2) then val2
                                         else getVal id rest;
    
    fun newinterp (AST_NUM(n)) (ev:env) = NUM (n)
      | newinterp (AST_ID(id)) ev = 
                let fun process(THUNK(tm,oldev)) = newinterp tm oldev
                      | process(other) = other
                in process(getVal id ev)
                end
      | newinterp (AST_BOOL(bval)) ev = BOOL(bval)
      | newinterp (AST_FUN(param,body)) ev = CLOSURE(param,body,ev)
      | newinterp (AST_SUCC) ev = SUCC
      | newinterp (AST_PRED) ev = PRED
      | newinterp (AST_ISZERO) ev = ISZERO
      | newinterp (AST_ERROR str) ev = ERROR ("In parse", NUM 0)
      | newinterp (AST_REC(name,body)) ev = 
                let val nuev = update ev name (THUNK(AST_REC(name,body),ev))
                in newinterp body nuev
                end
      | newinterp (AST_IF(test,yesval,noval)) ev = 
            let val testval = newinterp test ev;
                fun trueval(BOOL(true)) = true
                  | trueval(x) = false;
                fun falseval(BOOL(false)) = true
                  | falseval(x) = false;
            in if trueval(testval) then newinterp yesval ev
                    else if falseval(testval) then newinterp noval ev
                    else ERROR ("if cond not bool",testval)
            end
      | newinterp (AST_APP(func,arg)) ev = 
            let val evalfunc = newinterp func ev;
                val evalarg = newinterp arg ev;
                fun eval(SUCC,NUM(n)) = NUM(n+1)
                  | eval(PRED,NUM(n)) = if n > 0 then NUM (n-1)
                                                 else NUM 0
                  | eval(ISZERO,NUM(n)) = if n = 0 then BOOL(true)
                                                   else BOOL(false)
                  | eval(CLOSURE(param,body,fev),ERROR (s,v)) = ERROR ((s^" in arg"),v)
                  | eval(CLOSURE(param,body,fev),arg) =  
                                            let val nuev = update fev param arg
                                            in newinterp body nuev
                                            end
                  | eval(fst,snd) = ERROR ("no fcn",fst)
             in 
    	     eval(evalfunc,evalarg) 
             end;