CS118(2) homework #1

Haskell familiarization

Here is a basic lambda calculus interpreter written in Haskell.

The interpeter evaluates an Expression with respect to an Environment, which is a list of bindings of Names to Values. The only kinds of Values are Numbers, Functions, and Errors.

Notes

Num is distinct from the standard type class Num.
An Application applies a function to an Expression.
The function lookup and type Maybe with constructors Nothing and Just are defined in the Standard Prelude.

The interpreter, interp.hs

interp :: Expr -> Env -> Val
type Name = String
type Env = [(Name,Val)]

-- kinds of expression

data Expr = Con Int        -- integer constant
          | Var Name       -- variable
          | Lam Name Expr  -- lambda expression
          | Add Expr Expr  -- sum of two values
          | App Expr Expr  -- apply one value to another

-- kinds of value

data Val = Num Int
         | Fun (Val->Val)
         | Err

-- how to print a value

instance Show Val where
        show (Num x) = show x
        show (Fun _) = "<function>"
        show Err     = "<bad>"

-- interpreter proper
--      e   environment
--      v   value
--      x,y integer
--      s   string
--      f   Haskell function
--      t,u expression

interp (Con x) e = Num x
interp (Var s) e =
        case lookup s e of
        Just v -> v
        Nothing -> Err
interp (Lam s u) e = Fun (\v->interp u ((s,v):e))
interp (Add t u) e =
        case (interp t e, interp u e) of
        (Num x, Num y) -> Num (x+y)
        _ -> Err
interp (App t u) e = 
        case (interp t e, interp u e) of
        (Fun f, v) -> f v
        _ -> Err
interp _ e = Err

test t = interp t []

test1 = test (Con 0)
test2 = test (App (Lam "x" (Add (Con 1) (Var "x"))) (Con 2))

Exercises

Exercises 1,2,3 are confidence builders; 4 involves understanding first class functions, scope and environment; 5 combines compiler technique and language drill; 6 goes mano a mano with type classes.
  1. Expand the kinds of expression to include other arithmetic operators.
  2. Add a conditional operator, such as Tst Expr Expr Expr, which evaluates the second or third expression according as the value of the first, which must evaluate to a Con, is positive or not.
  3. Make Errors deliver informative diagnostics.
  4. Add a fixpoint operator Fix Name Expr to name a recursive function, in the same way as does let in
        let fact = \n->if n==0 then 1 else n*fact(n-1)
    
  5. Write a parser with signature
        parse :: String -> Expr
    
    to create Expressions from strings in a more readable language. The language should be recursively defined, using parentheses for grouping.
  6. The resistance, R, of a series-parallel network of electrical resistors is computed by repeated application of formulas for combining two resistances R1 and R2: In the O. M. Lord* notation, infix * stands for series combination and + for parallel. Using the datatype
        data Ohm = Ohm Double
    
    create workable definitions for + and *, and arrange that the result of executing
        r1    = Ohm 1
        r3    = Ohm 3
        main = [r1*r1, r1+r1, r1+r3, r1*(r3+r3+r3)]
    
    prints as [2.0, 0.5, 0.75, 2.0]
* Initials O. M. stand for Ohm My.