Closures
Closures implement static scoping by including an environment variable in the function value.
Note
While this fixes static scoping, we cannot use recursion with closures.
data FBAE where
Closure :: String -> FBAE -> Env -> FBAE
We also define a value type that is returned by the interpreter. This keeps the same values, but instead of defining lambda as a value, we define a closure as a value.
data FBAEVal where
NumV :: Int -> FBAEVal
ClosureV :: String -> FBAE -> Env -> FBAEVal -- This is new!
Within the interpreter, the only thing that really changes is the type we return and capture and what we return when evaluating a lambda.
eval :: Env -> FBAE -> Maybe FBAEVal
eval e (Num n) = NumV n
eval e (Plus l r) = do { (NumV l') <- eval e l;
(NumV r') <- eval e r;
return (NumV l'+r')
}
eval e (Lambda i s) = return (ClosureV i s e)
eval e (App f a) = do { (Closure i s e') <- eval e f;
a' <- eval e a;
-- Eval using the closure's environment instead of the current environment.
eval (i,a'):e' s
}
Note
While while is done with a copy in this implementation, and most other functional implementations. This is commonly done in other languages by pointing to stack space.