Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env...

20
CMPS 112: Spring 2019 Comparative Programming Languages Owen Arden UC Santa Cruz Environments and closures Based on course materials developed by Nadia Polikarpova Roadmap Past three weeks: How do we use a functional language? Next three weeks: How do we implement a functional language? … in a functional language (of course) This week: Interpreter How do we evaluate a program given its abstract syntax tree (AST)? How do we prove properties about our interpreter (e.g. that certain programs never crash)? 2 The Nano Language Features of Nano: 1. Arithmetic expressions 2. Variables and let-bindings 3. Functions 4. Recursion 3

Transcript of Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env...

Page 1: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

CMPS 112: Spring 2019

Comparative Programming Languages

Owen Arden

UC Santa Cruz

Environments and closures

Based on course materials developed by Nadia Polikarpova

RoadmapPast three weeks:

• How do we use a functional language?

Next three weeks:

• How do we implement a functional language? • … in a functional language (of course)

This week: Interpreter

• How do we evaluate a program given its abstract syntax tree (AST)? • How do we prove properties about our interpreter (e.g. that certain programs

never crash)?

!2

The Nano LanguageFeatures of Nano:

1. Arithmetic expressions

2. Variables and let-bindings

3. Functions 4. Recursion

!3

Page 2: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Reminder: CalculatorArithmetic expressions:

e::=n|e1+e2|e1-e2|e1*e2

Example:

4+13==>17

!4

Reminder: CalculatorHaskell datatype to represent arithmetic expressions:

dataExpr=NumInt|AddExprExpr|SubExprExpr|MulExprExpr

Haskell function to evaluate an expression:

eval::Expr->Inteval(Numn)=n

eval(Adde1e2)=evale1+evale2eval(Sube1e2)=evale1-evale2eval(Mule1e2)=evale1*evale2

!5

Reminder: CalculatorAlternative representation:

dataBinop=Add|Sub|Mul

dataExpr=NumInt--number|BinBinopExprExpr--binaryexpression

Evaluator for alternative representation:

eval::Expr->Inteval(Numn)=neval(BinAdde1e2)=evale1+evale2eval(BinSube1e2)=evale1-evale2eval(BinMule1e2)=evale1*evale2

!6

Page 3: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

The Nano LanguageFeatures of Nano:

1. Arithmetic expressions [done] 2. Variables and let-bindings

3. Functions 4. Recursion

!7

Extension: variablesLet’s add variables and let bindings!

e::=n|x|e1+e2|e1-e2|e1*e2|letx=e1ine2

Example:

letx=4+13in--17

lety=7-5in--2x*y

==>34

!8

Extension: variablesHaskell representation:

dataExpr=NumInt--number|???--variable|BinBinopExprExpr--binaryexpression|???--letexpression

!9

Page 4: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Extension: variablestypeId=String

dataExpr=NumInt--number|VarId--variable|BinBinopExprExpr--binaryexpression|LetIdExprExpr--letexpression

Haskell function to evaluate an expression:

eval::Expr->Inteval(Numn)=neval(Varx)=???...

!10

Extension: variablestypeId=String

dataExpr=NumInt--number|VarId--variable|BinBinopExprExpr--binaryexpression|LetIdExprExpr--letexpression

Haskell function to evaluate an expression:

eval::Expr->Inteval(Numn)=neval(Varx)=???...

!11

How do we evaluate a variable?

We have to remember which value it was bound to!

EnvironmentAn expression is evaluated in an environment, which maps all its free variables to values

Examples:

x*y=[x:17,y:2]=>34

x*y=[x:17]=>Error:unboundvariabley

x*(lety=2iny)=[x:17]=>34

!12

• How should we represent the environment?

• Which operations does it support?

Page 5: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Extension: variables

!13

http://tiny.cc/cmps112-vars-ind

Extension: variables

!14

http://tiny.cc/cmps112-vars-grp

Environment: APITo evaluate letx=e1ine2 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

typeValue=Int

dataEnv=...--representationnotthatimportant

--|Addanewbindingadd::Id->Value->Env->Env

--|Lookupthemostrecentlyaddedbindinglookup::Id->Env->Value

!15

Page 6: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Evaluating expressionsBack to our expressions… now with environments!

dataExpr=NumInt--number|VarId--variable|BinBinopExprExpr--binaryexpression|LetIdExprExpr--letexpression

!16

Evaluating expressionsHaskell function to evaluate an expression:

eval::Env->Expr->Valueevalenv(Numn)=nevalenv(Varx)=lookupxenvevalenv(Binope1e2)=fv1v2wherev1=evalenve1v2=evalenve2f=caseopofAdd->(+)Sub->(-)Mul->(*)evalenv(Letxe1e2)=evalenv'e2wherev=evalenve1env'=addxvenv

!17

Example evaluationNano expression

letx=1inlety=(letx=2inx)+xinletx=3inx+y

is represented in Haskell as:

exp1=Let"x"(Num1)(Let"y"(Add(Let"x"(Num2)(Varx))(Varx))(Let"x"(Num3)(Add(Varx)(Vary))))

!18

exp3

exp2

exp4

exp5

Page 7: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Example evaluationeval[]exp1=>eval[](Let"x"(Num1)exp2)=>eval[("x",eval[](Num1))]exp2=>eval[("x",1)](Let"y"(Addexp3exp4)exp5)=>eval[("y",(eval[("x",1)](Addexp3exp4))),("x",1)]exp5=>eval[("y",(eval[("x",1)](Let"x"(Num2)(Var"x"))+eval[("x",1)](Var"x"))),("x",1)]exp5=>eval[("y",(eval[("x",2),("x",1)](Var"x")--newbindingforx+1)),("x",1)]exp5=>eval[("y",(2--uselatestbindingforx+1)),("x",1)]exp5=>eval[("y",3),("x",1)](Let"x"(Num3)(Add(Var"x")(Var"y")))

!19

Example evaluation=>eval[("y",3),("x",1)](Let"x"(Num3)(Add(Var"x")(Var“y")))=>eval[("x",3),("y",3),("x",1)]--newbindingforx(Add(Var"x")(Var"y"))=>eval[("x",3),("y",3),("x",1)](Var"x")+eval[("x",3),("y",3),("x",1)](Var"y")=>3+3=>6

!20

Example evaluationSame evaluation in a simplified format (Haskell Expr terms replaced by their “pretty-printed version”):

eval[]{letx=1inlety=(letx=2inx)+xinletx=3inx+y}=>eval[x:(eval[]1)]{lety=(letx=2inx)+xinletx=3inx+y}=>eval[x:1]{lety=(letx=2inx)+xinletx=3inx+y}=>eval[y:(eval[x:1]{(letx=2inx)+x}),x:1]{letx=3inx+y}=>eval[y:(eval[x:1]{letx=2inx}+eval[x:1]{x}),x:1]{letx=3inx+y}--newbindingforx:=>eval[y:(eval[x:2,x:1]{x}+eval[x:1]{x}),x:1]{letx=3inx+y}--uselatestbindingforx:=>eval[y:(2+eval[x:1]{x}),x:1]{letx=3inx+y}=>eval[y:(2+1),x:1]{letx=3inx+y} !21

Page 8: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Example evaluation=>eval[y:(2+1),x:1]{letx=3inx+y}=>eval[y:3,x:1]{letx=3inx+y}--newbindingforx:=>eval[x:3,y:3,x:1]{x+y}=>eval[x:3,y:3,x:1]x+eval[x:3,y:3,x:1]y--uselatestbindingforx:=>3+3=>6

!22

Runtime errorsHaskell function to evaluate an expression:

eval::Env->Expr->Valueevalenv(Numn)=nevalenv(Varx)=lookupxenv--canfail!evalenv(Binope1e2)=fv1v2wherev1=evalenve1v2=evalenve2f=caseopofAdd->(+)Sub->(-)Mul->(*)evalenv(Letxe1e2)=evalenv'e2wherev=evalenve1env'=addxvenv

How do we make sure lookup doesn’t cause a run-time error?

!23

Free vs bound variablesIn evalenve, env must contain bindings for all free variables of e!

• an occurrence of x is free if it is not bound

• an occurrence of x is bound if it’s inside e2 where letx=e1ine2

• evaluation succeeds when an expression is closed!

!24

Page 9: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

QUIZ

!25

http://tiny.cc/cmps112-free-ind

QUIZ

!26

http://tiny.cc/cmps112-free-grp

The Nano LanguageFeatures of Nano:

1. Arithmetic expressions [done] 2. Variables and let-bindings [done] 3. Functions 4. Recursion

!27

Page 10: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Extension: functionsLet’s add lambda abstraction and function application!

e::=n|x|e1+e2|e1-e2|e1*e2|letx=e1ine2|\x->e--abstraction|e1e2--application

Example:

letc=42inletcTimes=\x->c*xincTimes2==>84

!28

Extension: functionsHaskell representation:

dataExpr=NumInt--number|VarId--variable|BinBinopExprExpr--binaryexpression|LetIdExprExpr--letexpression|???--abstraction|???--application

!29

Extension: functionsHaskell representation:

dataExpr=NumInt--number|VarId--variable|BinBinopExprExpr--binaryexpression|LetIdExprExpr--letexpression|LamIdExpr--abstraction|AppExprExpr--application

!30

Page 11: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Extension: functionsExample:

letc=42inletcTimes=\x->c*xincTimes2

represented as:

Let"c"(Num42)(Let"cTimes"(Lam"x"(Mul(Var"c")(Var"x")))(App(Var"cTimes")(Num2)))

!31

Extension: functionsExample:

letc=42inletcTimes=\x->c*xincTimes2

How should we evaluate this expression?

eval[]{letc=42inletcTimes=\x->c*xincTimes2}=>eval[c:42]{letcTimes=\x->c*xincTimes2}=>eval[cTimes:???,c:42]{cTimes2}

What is the value of cTimes???

!32

Rethinking our valuesUntil now: a program evaluates to an integer (or fails)

typeValue=Int

typeEnv=[(Id,Value)]

eval::Env->Expr->Value

!33

Page 12: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Rethinking our valuesWhat do these programs evaluate to?

(1)\x->2*x==>???

(2)letf=\x->\y->2*(x+y)inf5

==>???

Conceptually, (1) evaluates to itself (not exactly, see later). while (2) evaluates to something equivalent to \y->2*(5+y)

!34

Rethinking our valuesNow: a program evaluates to an integer or a lambda abstraction (or fails)

• Remember: functions are first-class values

Let’s change our definition of values!

dataValue=VNumInt|VLam???--Whatinfodoweneedtostore?--OthertypesstaythesametypeEnv=[(Id,Value)]

eval::Env->Expr->Value

!35

Function valuesHow should we represent a function value?

letc=42inletcTimes=\x->c*xincTimes2

We need to store enough information about cTimes so that we can later evaluate any application of cTimes (like cTimes2)!

First attempt:

dataValue=VNumInt

|VLamIdExpr--formal+body

!36

Page 13: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Function valuesLet’s try this!

eval[]{letc=42inletcTimes=\x->c*xincTimes2}=>eval[c:42]{letcTimes=\x->c*xincTimes2}=>eval[cTimes:(\x->c*x),c:42]{cTimes2}--evaluatethefunction:=>eval[cTimes:(\x->c*x),c:42]{(\x->c*x)2}--evaluatetheargument,bindtox,evaluatebody:=>eval[x:2,cTimes:(\x->c*x),c:42]{c*x}=>42*2=>84

Looks good… can you spot a problem?!37

QUIZ

!38

http://tiny.cc/cmps112-cscope-ind

QUIZ

!39

http://tiny.cc/cmps112-cscope-grp

Page 14: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Static vs Dynamic ScopingWhat we want:

letc=42inletcTimes=\x->c*xinletc=5incTimes2=>84

Lexical (or static) scoping:

• each occurrence of a variable refers to the most recent binding in the program text

• definition of each variable is unique and known statically • good for readability and debugging: don’t have to figure out where a variable

got “assigned”

!40

Static vs Dynamic ScopingWhat we don’t want:

letc=42inletcTimes=\x->c*xinletc=5incTimes2=>10

Dynamic scoping:

• each occurrence of a variable refers to the most recent binding during program execution

• can’t tell where a variable is defined just by looking at the function body • nightmare for readability and debugging:

!41

Static vs Dynamic ScopingDynamic scoping:

• each occurrence of a variable refers to the most recent binding during program execution

• can’t tell where a variable is defined just by looking at the function body • nightmare for readability and debugging:

letcTimes=\x->c*xinletc=5inletres1=cTimes2in--==>10

letc=10inletres2=cTimes2in--==>20!!!res2-res1

!42

Page 15: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Function valuesdataValue=VNumInt|VLamIdExpr--formal+body

This representation can only implement dynamic scoping!

letc=42in

letcTimes=\x->c*xin

letc=5in

cTimes2

evaluates as:

eval[]

{letc=42inletcTimes=\x->c*xinletc=5incTimes2}

!43

Function valueseval[]{letc=42inletcTimes=\x->c*xinletc=5incTimes2}=>eval[c:42]{letcTimes=\x->c*xinletc=5incTimes2}=>eval[cTimes:(\x->c*x),c:42]{letc=5incTimes2}=>eval[c:5,cTimes:(\x->c*x),c:42]{cTimes2}=>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}--latestbindingforcis5!=>5*2=>10

Lesson learned: need to remember what c was bound to when cTimes was defined!

• i.e. “freeze” the environment at function definition

!44

ClosuresTo implement lexical scoping, we will represent function values as closures

Closure = lambda abstraction (formal + body) + environment at function definition

dataValue=VNumInt|VClosEnvIdExpr--env+formal+body

!45

Page 16: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

ClosuresOur example:

eval[]{letc=42inletcTimes=\x->c*xinletc=5incTimes2}=>eval[c:42]{letcTimes=\x->c*xinletc=5incTimes2}--remembercurrentenv:=>eval[cTimes:<[c:42],\x->c*x>,c:42]{letc=5incTimes2}=>eval[c:5,cTimes:<[c:42],\x->c*x>,c:42]{cTimes2}=>eval[c:5,cTimes:<[c:42],\x->c*x>,c:42]{<[c:42],\x->c*x>2}--restoreenvtotheoneinsidetheclosure,thenbind2tox:=>eval[x:2,c:42]{c*x}=>42*2=>84

!46

QUIZ

!47http://tiny.cc/cmps112-env-ind

QUIZ

!48http://tiny.cc/cmps112-env-grp

Page 17: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Free vs bound variables• An occurrence of x is free if it is not bound

• An occurrence of x is bound if it’s inside

◦ e2 where letx=e1ine2

◦ e where \x->e • A closure environment has to save all free variables of a function definition!

leta=20inletf=\x->lety=x+1inletg=\z->y+zina+gx--aistheonlyfreevariable!in...

!49

EvaluatorLet’s modify our evaluator to handle functions!

dataValue=VNumInt|VClosEnvIdExpr--env+formal+bodyeval::Env->Expr->Valueevalenv(Numn)=VNumn--mustwrapinVNumnow!evalenv(Varx)=lookupxenvevalenv(Binope1e2)=VNum(fv1v2)where(VNumv1)=evalenve1(VNumv2)=evalenve2f=...--asbeforeevalenv(Letxe1e2)=evalenv'e2wherev=evalenve1env'=addxvenvevalenv(Lamxbody)=???--constructaclosureevalenv(Appfunarg)=???--evalfun,thenarg,thenapply

!50

EvaluatorEvaluating functions:

• Construct a closure: save environment at function definition • Apply a closure: restore saved environment, add formal, evaluate the body

eval::Env->Expr->Value...evalenv(Lamxbody)=VClosenvxbodyevalenv(Appfunarg)=evalbodyEnvbodywhere(VClosclosEnvxbody)=evalenvfun--evalfunctiontoclosurevArg=evalenvarg--evalargumentbodyEnv=addxvArgclosEnv

!51

Page 18: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

EvaluatorEvaluating functions:

• Construct a closure: save environment at function definition • Apply a closure: restore saved environment, add formal, evaluate the body

eval::Env->Expr->Value...evalenv(Lamxbody)=VClosenvxbodyevalenv(Appfunarg)=letvArg=evalenvarg--evalargumentletbodyEnv=addxvArgclosEnvcase(evalenvfun)of--evalfunctiontoclosure(VClosclosEnvxbody)->evalbodyEnvbody_->???

!52

Quiz

!53

http://tiny.cc/cmps112-enveval-ind

Quiz

!54

http://tiny.cc/cmps112-enveval-grp

Page 19: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Evaluatoreval[]

{letf=\x->x+yinlety=10inf5}

=>eval[f:<[],\x->x+y>]

{lety=10inf5}

=>eval[y:10,f:<[],\x->x+y>]

{f5}

=>eval[y:10,f:<[],\x->x+y>]

{<[],\x->x+y>5}

=>eval[x:5]--envgotreplacedbyclosureenv+formal!

{x+y}--yisunbound!

!55

Quiz

!56

http://tiny.cc/cmps112-enveval2-ind

Quiz

!57

http://tiny.cc/cmps112-enveval2-grp

Page 20: Comparative Programming Languagesowen/courses/cmps112/spr19/slides/env.key.pdfIn eval env e, env must contain bindings for all free variables of e! • an occurrence of x is free if

Evaluatoreval[]

{letf=\n->n*f(n-1)inf5}

=>eval[f:<[],\n->n*f(n-1)>]

{f5}

=>eval[f:<[],\n->n*f(n-1)>]

{<[],\n->n*f(n-1)>5}

=>eval[n:5]--envgotreplacedbyclosureenv+formal!

{n*f(n-1)}--fisunbound!

Lesson learned: to support recursion, we need a different way of constructing the closure environment!

!58