# Interpreting Functions, Continued --- CS 135 // 2021-04-20 ## Assignment 2 - Extended deadline to Thursday, 4/22 before class - A couple faulty tests on codePost + Did not account for all possible desugaring # Questions ## ...about anything? # Review ## Defining Functions - Last time we added **function** to our language - `FnDef` represents a function definition ```haskell -- name param body data FnDef = FnDef Identifier Identifier Expr ``` - We also updated `Expr` include: ```haskell data Expr = ... -- name arg | FnApp Identifier Expr | Id Identifier ``` ## Updated `interp` - We required functions to be declared **before** they are interpreted, so `interp` became: ```haskell interp :: [FnDef] -> Expr -> Value interp fs exp = case exp of ... FnApp f arg -> let (FnDef _ param body) = lookupFn f fs in interp fs (subst param arg body) ... ``` - `lookupFn` finds a function definition from a list ## The `subst` Function - `subst` substitutes an argument for a parameter in the function body ```haskell subst :: Identifier -> Expr -> Expr -> Expr subst param arg body = let sub = subst param arg in case body of Plus x y -> Plus (sub x) (sub y) Mult x y -> Mult (sub x) (sub y) Num n -> Num n TrueE -> TrueE FalseE -> FalseE If cnd thn els -> If (sub cnd) (sub thn) (sub els) FnApp f x -> FnApp f (sub x) Id x -> if param == x then arg else Id x ``` # Reflections ## Reflection Questions - Suppose I have a function named `double` that doubles its parameter: ```haskell -- double x = x + x FnDef "double" "x" (Plus (Id "x") (Id "x")) ``` - Now suppose I interpret `(double (+ 1 2))` + How many times is `(+ 1 2)` evaluated? + Twice! Applying the function produces: * `(+ (+ 1 2) (+ 1 2))` - This is called **lazy** evaluation ## Reflection Questions - Suppose I have the following two function definitions: ```haskell -- double x = x + x FnDef "double" "x" (Plus (Id "x") (Id "x")) -- quad x = double (double x) FnDef "quad" "x" (FnApp "double" (FnApp "double" (Id "x")) ``` - Will interpreting `(quad 10)` actually work? ## Reflection Questions - Now suppose I have the following function definition: ```haskell -- foo x = if True then x else (x+x) FnDef "foo" "x" (If #t (Id "x") (Plus (Id "x") (Id "x"))) ``` - When I call `(foo 10)`, how many times is `10` substituted for `x`? ## Reflection Questions - Do any of you take issue with how our implementation of `interp` handles identifiers? ```haskell interp :: [FnDef] -> Expr -> Value interp fs exp = case exp of ... Id id -> error "*** unbound variable" ``` - When the interpreter finds a variable, shouldn't it "look up" what the value is from somewhere? ## Introducing the # Environment ## The Environment - We can **defer** substitution of identifiers by creating a lookup table mapping names to values - Such a list of bindings is commonly called the **environment** - We will use the following type for this: ```haskell type Env = [(Identifier,Value)] ``` ## Updating `interp` with `Env` - Now our `interp` function will have the following signature: ```haskell interp :: Env -> [FnDef] -> Expr -> Value ``` - We also need a function to lookup a binding: ```haskell lookupEnv :: Identifier -> Env -> Value lookupEnv x [] = error ("*** unbound variable " ++ x) lookupEnv x ((id,val):bs) | x == id = val | otherwise = lookupEnv x bs ``` ## Updating `interp` ```haskell interp :: Env -> [FnDef] -> Expr -> Value interp env fns exp = case exp of ... FnApp f arg -> let (FnDef _ param body) = lookupFn f fs arg' = interp env fs arg env' = (param,arg'):env in interp env' fs body ``` ## Dynamic Scope - Notice that we keep accumulating bindings in our environment every time a function is called - Consider the following functions: ```haskell foo x = bar 1 bar y = x + y ``` - What happens when I call `foo 2` in our language? - This is called **dynamic scope** since the value of a variable depends on the entire history of calls ## Updating `interp` Again ```haskell interp :: Env -> [FnDef] -> Expr -> Value interp env fns exp = case exp of ... FnApp f arg -> let (FnDef _ param body) = lookupFn f fs arg' = interp env fs arg in interp [(param,arg')] fs body ``` - Notice we are forcing functions to use a new environment when they are called - This is called **static scope** so that variables can only depend on their local, lexical scope - Our language now has similar behavior to our substitution interpreter # Functions Anywhere