6 Env
6 Env
Foundations of Programming
Languages
Next weeks:
WHY??
2
The Nano Language
Features of Nano:
1. Arithmetic expressions
2. Variables and let-bindings
3. Functions
4. Recursion
3
Reminder: Calculator
Arithmetic expressions:
e ::= n
| e1 + e2
| e1 - e2
| e1 * e2
Example:
4 + 13
==> 17
4
Reminder: Calculator
Haskell datatype to represent arithmetic expressions:
data Expr = Num Int
| Add Expr Expr
| Sub Expr Expr
| Mul Expr Expr
5
Reminder: Calculator
Alternative representation:
data Binop = Add | Sub | Mul
6
The Nano Language
Features of Nano:
7
Extension: variables
Let’s add variables and let bindings!
e ::= n | x
| e1 + e2 | e1 - e2 | e1 * e2
| let x = e1 in e2
Example:
let x = 4 + 13 in -- 17
let y = 7 - 5 in -- 2
x * y
==> 34
8
Extension: variables
Haskell representation:
data Expr = Num Int -- number
| ??? -- variable
| Bin Binop Expr Expr -- binary expression
| ??? -- let expression
9
Extension: variables
type Id = String
10
Example Expression
Nano expression
let x = 1 in
let y = (let x = 2 in x) + x in
let x = 3 in
x + y
is represented in Haskell as:
exp1 = Let "x"
(Num 1) exp2
(Let "y"
exp3
(Add
(Let "x" (Num 2) (Var x))
exp4 (Var x))
12
Extension: variables
We have to remember
which value it was bound to!
13
Environment
An expression is evaluated in an environment, which
maps the free variables of the expression to values
Examples:
• How should we represent the
x * y environment?
=[x:17, y:2]=> 34 • Which operations does it support?
x * y
=[x:17]=> Error: unbound variable y
x * (let y = 2 in y)
=[x:17]=> 34
14
Extension: variables
http://tiny.cc/cse116-vars-ind
15
Extension: variables
http://tiny.cc/cse116-vars-grp
16
Environment: API
To evaluate
let x = e1 in e2 in env:
• evaluate e2 in an extended environment env + [x:v]
• where v is the result of evaluating e1
To evaluate x in env:
• lookup the most recently added binding for x
17
Environment: Implementation
type Value = Int
type Id = String
18
Evaluating expressions
Back to our expressions… now with environments!
data Expr = Num Int -- number
| Var Id -- variable
| Bin Binop Expr Expr -- binary expression
| Let Id Expr Expr -- let expression
19
Evaluating expressions
Haskell function to evaluate an expression:
eval :: Env -> Expr -> Value
eval env (Num n) = n
eval env (Var x) = lookup x env
eval env (Bin op e1 e2) = f v1 v2
where
v1 = eval env e1
v2 = eval env e2
f = case op of
Add -> (+)
Sub -> (-)
Mul -> (*)
eval env (Let x e1 e2) = eval env' e2
where
v = eval env e1
env' = add x v env
20
Example evaluation
Nano expression
let x = 1 in
let y = (let x = 2 in x) + x in
let x = 3 in
x + y
is represented in Haskell as:
exp1 = Let "x"
(Num 1) exp2
(Let "y"
exp3
(Add
(Let "x" (Num 2) (Var x))
exp4 (Var x))
23
Example evaluation
Same evaluation in a simplified format (Haskell Expr terms replaced by their “pretty-
printed version”):
eval []
{let x = 1 in let y = (let x = 2 in x) + x in let x = 3 in x + y}
=> eval [x:(eval [] 1)]
{let y = (let x = 2 in x) + x in let x = 3 in x + y}
=> eval [x:1]
{let y = (let x = 2 in x) + x in let x = 3 in x + y}
=> eval [y:(eval [x:1] {(let x = 2 in x) + x}), x:1]
{let x = 3 in x + y}
=> eval [y:((eval [x:1] {let x = 2 in x}) + (eval [x:1] {x})), x:1]
{let x = 3 in x + y}
-- new binding for x:
=> eval [y:((eval [x:2,x:1] {x}) + (eval [x:1] {x})), x:1]
{let x = 3 in x + y}
-- use latest binding for x:
=> eval [y:( 2 + (eval [x:1] {x})), x:1]
{let x = 3 in x + y}
=> eval [y:( 2 + 1) , x:1]
{let x = 3 in x + y} 24
Example evaluation
=> eval [y:( 2 + 1) , x:1]
{let x = 3 in x + y}
=> 6
25
Can runtime errors occur?
Haskell function to evaluate an expression:
eval :: Env -> Expr -> Value
eval env (Num n) = n
eval env (Var x) = lookup x env
eval env (Bin op e1 e2) = f v1 v2
where
v1 = eval env e1
v2 = eval env e2
f = case op of
Add -> (+)
Sub -> (-)
Mul -> (*)
eval env (Let x e1 e2) = eval env' e2
where
v = eval env e1
env' = add x v env
27
QUIZ
http://tiny.cc/cse116-free-ind
28
QUIZ
http://tiny.cc/cse116-free-grp
29
The Nano Language
Features of Nano:
30
Extension: functions
Let’s add lambda abstraction and function application!
e ::= n | x
| e1 + e2 | e1 - e2 | e1 * e2
| let x = e1 in e2
| \x -> e -- abstraction
| e1 e2 -- application
Example:
let c = 42 in
let cTimes = \x -> c * x in
cTimes 2
==> 84
31
Extension: functions
Haskell representation:
data Expr = Num Int -- number
| Var Id -- variable
| Bin Binop Expr Expr -- binary expression
| Let Id Expr Expr -- let expression
| ??? -- abstraction
| ??? -- application
32
Extension: functions
Haskell representation:
data Expr = Num Int -- number
| Var Id -- variable
| Bin Binop Expr Expr -- binary expression
| Let Id Expr Expr -- let expression
| Lam Id Expr -- abstraction
| App Expr Expr -- application
33
Extension: functions
Example:
let c = 42 in
let cTimes = \x -> c * x in
cTimes 2
represented as:
Let "c"
(Num 42)
(Let "cTimes"
(Lam "x" (Mul (Var "c") (Var "x")))
(App (Var "cTimes") (Num 2)))
34
Extension: functions
Example:
let c = 42 in
let cTimes = \x -> c * x in
cTimes 2
36
Rethinking our values
What do these programs evaluate to?
(1)
\x -> 2 * x
==> ???
(2)
let f = \x -> \y -> 2 * (x + y) in
f 5
==> ???
37
Rethinking our values
Now: a program evaluates to an integer or a lambda abstraction (or fails)
38
Function values
How should we represent a function value?
let c = 42 in
let cTimes = \x -> c * x in
cTimes 2
We need to store enough information about cTimes so that we can later evaluate
any application of cTimes (like cTimes 2)!
First attempt:
data Value = VNum Int
| VLam Id Expr -- formal + body
39
Function values
Let’s try this!
eval []
{let c = 42 in let cTimes = \x -> c * x in cTimes 2}
=> eval [c:42]
{let cTimes = \x -> c * x in cTimes 2}
=> eval [cTimes:(\x -> c*x), c:42]
{cTimes 2}
-- evaluate the function:
=> eval [cTimes:(\x -> c*x), c:42]
{(\x -> c * x) 2}
-- evaluate the argument, bind to x, evaluate body:
=> eval [x:2, cTimes:(\x -> c*x), c:42]
{c * x}
=> 42 * 2
=> 84
http://tiny.cc/cse116-cscope-ind
41
QUIZ
http://tiny.cc/cse116-cscope-grp
42
Static vs Dynamic Scoping
What we want:
let c = 42 in
let cTimes = \x -> c * x in
let c = 5 in
cTimes 2
=> 84
43
Static vs Dynamic Scoping
What we don’t want:
let c = 42 in
let cTimes = \x -> c * x in
let c = 5 in
cTimes 2
=> 10
Dynamic scoping:
44
Static vs Dynamic Scoping
Dynamic scoping:
45
Function values
data Value = VNum Int
| VLam Id Expr -- formal + body
This representation can only implement dynamic scoping!
let c = 42 in
let cTimes = \x -> c * x in
let c = 5 in
cTimes 2
evaluates as:
eval []
{let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2}
46
Function values
eval []
{let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2}
=> eval [c:42]
{let cTimes = \x -> c * x in let c = 5 in cTimes 2}
=> eval [cTimes:(\x -> c*x), c:42]
{let c = 5 in cTimes 2}
=> eval [c:5, cTimes:(\x -> c*x), c:42]
{cTimes 2}
=> eval [c:5, cTimes:(\x -> c*x), c:42]
{(\x -> c * x) 2}
=> eval [x:2, c:5, cTimes:(\x -> c*x), c:42]
{c * x}
-- latest binding for c is 5!
=> 5 * 2
=> 10
Lesson learned: need to remember what c was bound to when cTimes was
defined!
• i.e. remember the environment at function definition
47
Closures
To implement lexical scoping, we will represent function values as closures
48
Closures
Our example:
eval []
{let c = 42 in let cTimes = \x -> c * x in let c = 5 in cTimes 2}
=> eval [c:42]
{let cTimes = \x -> c * x in let c = 5 in cTimes 2}
-- remember current env:
=> eval [cTimes:<[c:42], \x -> c*x>, c:42]
{let c = 5 in cTimes 2}
=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42]
{cTimes 2}
=> eval [c:5, cTimes:<[c:42], \x -> c*x>, c:42]
{<[c:42], \x -> c * x> 2}
-- restore env to the one inside the closure, then bind 2 to x:
=> eval [x:2, c:42]
{c * x}
=> 42 * 2
=> 84
49
QUIZ
http://tiny.cc/cse116-env-ind
50
QUIZ
http://tiny.cc/cse116-env-grp
51
Free vs bound variables
• An occurrence of x is bound if it’s inside
◦ e2 where let x = e1 in e2
◦ e where \x -> e
• An occurrence of x is free if it is not bound
• A closure environment has to save all free variables of a function definition!
let a = 20 in
let f =
\x -> let y = x + 1 in
let g = \z -> y + z in
a + g x -- a is the only free variable!
in ...
52
Evaluator
Let’s modify our evaluator to handle functions!
data Value = VNum Int
| VClos Env Id Expr -- env + formal + body
54
Evaluator
Evaluating functions:
55
Quiz
http://tiny.cc/cse116-enveval-ind
56
Quiz
http://tiny.cc/cse116-enveval-grp
57
Evaluator
eval []
{let f = \x -> x + y in let y = 10 in f 5}
=> eval [f:<[], \x -> x + y>]
{let y = 10 in f 5}
=> eval [y:10, f:<[], \x -> x + y>]
{f 5}
=> eval [y:10, f:<[], \x -> x + y>]
{<[], \x -> x + y> 5}
=> eval [x:5] -- env got replaced by closure env + formal!
{x + y} -- y is unbound!
58
Quiz
http://tiny.cc/cse116-enveval2-ind
59
Quiz
http://tiny.cc/cse116-enveval2-grp
60
Evaluator
eval []
{let f = \n -> n * f (n - 1) in f 5}
=> eval [f:<[], \n -> n * f (n - 1)>]
{f 5}
=> eval [f:<[], \n -> n * f (n - 1)>]
{<[], \n -> n * f (n - 1)> 5}
=> eval [n:5] -- env got replaced by closure env + formal!
{n * f (n - 1)} -- f is unbound!
61