Closures & Environments CS153: Compilers Greg Morrisett.

Slides:



Advertisements
Similar presentations
Semantics of PLs via Interpreters: Getting Started CS784: Programming Languages Prabhaker Mateti.
Advertisements

1 Programming Languages (CS 550) Mini Language Interpreter Jeremy R. Johnson.
Type Checking, Inference, & Elaboration CS153: Compilers Greg Morrisett.
Scheme in Scheme. Why implement Scheme in Scheme  Implementing a language is a good way to learn more about programming languages  Interpreters are.
Control-Flow Graphs & Dataflow Analysis CS153: Compilers Greg Morrisett.
Recap 1.Programmer enters expression 2.ML checks if expression is “well-typed” Using a precise set of rules, ML tries to find a unique type for the expression.
1 Programming Languages (CS 550) Lecture Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
Getting started with ML ML is a functional programming language. ML is statically typed: The types of literals, values, expressions and functions in a.
Functional Programming. Pure Functional Programming Computation is largely performed by applying functions to values. The value of an expression depends.
Cs784(TK)1 Semantics of Procedures and Scopes. Kinds of Scope Static or Lexical scope –determined by structure of program –Scheme, C++, Java, and many.
Functional programming: LISP Originally developed for symbolic computing Main motivation: include recursion (see McCarthy biographical excerpt on web site).
6.001 SICP SICP – Evaluation I Recitation 11/19/2004 Eval review Evaluation examples define lambda apply New language elements.
Functional programming: LISP Originally developed for symbolic computing First interactive, interpreted language Dynamic typing: values have types, variables.
Closure and Environment Compiler Baojian Hua
Introduction Even though the syntax of Scheme is simple, it can be very difficult to determine the semantics of an expression. Hacker’s approach: Run it.
CSE341: Programming Languages Structs, Implementing Languages, Implementing Higher-Order Functions Alan Borning (slides “borrowed” from Dan Grossman) Fall.
Cs7100(Prasad)L8Proc1 Procedures. cs7100(Prasad)L8Proc2 Primitive procedures  etc User-defined procedures –Naming a sequence of operations.
Functional Programming and Lisp. Overview In a functional programming language, functions are first class objects. In a functional programming language,
SICP Interpretation Parts of an interpreter Arithmetic calculator Names Conditionals and if Storing procedures in the environment Environment as.
CMSC 330: Organization of Programming Languages
Implementing a Dependently Typed λ -Calculus Ali Assaf Abbie Desrosiers Alexandre Tomberg.
Variables, Environments and Closures. Overview Touch on the notions of variable extent and scope Introduce the notions of lexical scope and dynamic.
CMSC 330: Organization of Programming Languages Functional Programming with OCaml.
CMSC 330: Organization of Programming Languages Operational Semantics a.k.a. “WTF is Project 4, Part 3?”
1 Programming Languages (CS 550) Lecture 4 Summary Functional Programming and Operational Semantics for Scheme Jeremy R. Johnson.
Interpreters and Higher-Order Functions CSE 413 Autumn 2008 Credit: CSE341 notes by Dan Grossman.
Scheme in Scheme 2. What’s next  Adding set!  Dynamic vs. lexical variable scope  Extending mcscheme v1 with libraries  Can mcscheme execute mcscheme?
CMSC 330: Organization of Programming Languages Operational Semantics.
3/8/20161 Programming Languages and Compilers (CS 421) Reza Zamani Based in part on slides by Mattox Beckman, as updated.
1 Vectors, binary search, and sorting. 2 We know about lists O(n) time to get the n-th item. Consecutive cons cell are not necessarily consecutive in.
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Winter 2013.
SICP Interpretation part 2 Store operators in the environment Environment as explicit parameter Defining new procedures.
Operational Semantics of Scheme
Lecture 4: Metacircles Eval Apply David Evans
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Spring 2017.
The interpreter.
Interpreters Study Semantics of Programming Languages through interpreters (Executable Specifications) cs7100(Prasad) L8Interp.
Env. Model Implementation
Closure conversion Compiling λ.
Original material by Eric Grimson
Tim Sheard Oregon Graduate Institute
Semantics of PLs via Interpreters: Getting Started
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Autumn 2018.
The Metacircular Evaluator
Objective caml Daniel Jackson MIT Lab for Computer Science 6898: Advanced Topics in Software Design March 18, 2002.
Dynamic Scoping Lazy Evaluation
The Metacircular Evaluator
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Zach Tatlock Winter 2018.
Procedures App B: SLLGEN 1.
3.4 Local Binding Recall Scheme's let: > (let ((x 5)‏ (y 6))
The Metacircular Evaluator (Continued)
Lecture 26: The Metacircular Evaluator Eval Apply
6.001 SICP Further Variations on a Scheme
Extending our interpreted language with Scheme’s set!
Chapter 1 Review: BNF Grammar for lists:
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Spring 2016.
Streams, Delayed Evaluation and a Normal Order Interpreter
3.6 Interpreter: Recursion
6.001 SICP Variations on a Scheme
Madhusudan Parthasarathy (madhu) 3112 Siebel Center
Chapter 3: Environment-Passing Interpreters
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Autumn 2017.
6.001 SICP Interpretation Parts of an interpreter
topics interpreters meta-linguistic abstraction eval and apply
Recursive Procedures and Scopes
Rehearsal: Lazy Evaluation Infinite Streams in our lazy evaluator
Principles of Programming Languages
Brett Wortzman Summer 2019 Slides originally created by Dan Grossman
CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Spring 2019.
Lecture 25: The Metacircular Evaluator Eval Apply
Presentation transcript:

Closures & Environments CS153: Compilers Greg Morrisett

"Functional" Languages: Scheme, Racket,, SML, Ocaml, Haskell, Clojure, Javascript, … Functions are first-class –not just for calling –can pass to other functions (map), return from functions (compose), place in data structures. Functions nest –A nested function can refer to variables bound in an outer function.

Nesting: let add = fun x -> (fun y -> y+x) let inc = add 1 (* = fun y -> y + 1 *) let dec = add ~1 (* = fun y -> y - 1 *) let compose = fun f -> fun g -> fun x -> f(g x) let id = compose inc dec (* = fun x -> inc(dec x) *) (* = fun x -> (fun y -> y+1)((fun y -> y-1) x) *) (* = fun x -> (fun y -> y+1)(x-1)) *) (* = fun x -> (x-1)+1 *) After calling add, we can't just throw away its arguments (or local variables) because those values are needed in the nested function that it returns.

Substitution-Based Semantics type exp = Int of int | Plus of exp*exp | Var of var | Lambda of var*exp | App of exp*exp let rec eval (e:exp) = match e with | Int i -> Int i | Plus(e1,e2) -> (match eval e1,eval e2 with | Int i,Int j -> Int(i+j)) | Var x -> error ("Unbound variable "^x) | Lambda(x,e) -> Lambda(x,e) | App(e1,e2) -> (match eval e1, eval e2 with (Lambda(x,e),v) -> eval (substitute v x e)))

Substitution-Based Semantics let rec subst (v:exp) (x:var) (e:exp) = match e with | Int i -> Int i | Plus(e1,e2) -> Plus(subst v x e1,subst v x e2) | Var y -> if y = x then v else Var y | Lambda(y,e) -> if y = x then Lambda(x,e) else Lambda(x,subst v x e) | App(e1,e2) -> App(subst v x e1,subst v x e2)

Example: App(App(Lambda(x,Lambda(y,Plus(Var x,Var y)),Int 3),Int 4) –App(Lambda(x,Lambda(y,Plus(Var x,Var y)),Int 3) Lambda(x,Lambda(y,Plus(Var x,Var y)) Int 3 eval(subst(Int 3) x )Lambda(y,Plus(Var x,Var y)))) –Lambda(y,Plus(Int 3,Var y)) Lambda(y,Plus(Int 3,Var y)) –Int 4 –subst(Int 4) y (Plus(Int 3,Var y)) –Plus(Int 3,Int 4) Int 3 Int 4 Int 7

Problems: Eval crawls over an expression. Substitute crawls over an expression. So eval (substitute v x e) is pretty stupid. Why not evaluate as we substitute?

First Attempt: type env = (exp * value) list let rec eval (e:exp) (env:env) : value = match e with | Int i -> Int i | Var x -> lookup env x | Lambda(x,e) -> Lambda(x,e) | App(e1,e2) -> (match eval e1 env, eval e2 env with | Lambda(x,e), v -> eval e ((x,v)::env))

Second Attempt: type env = (exp * value) list let rec eval (e:exp) (env:env) : value = match e with | Int i -> Int i | Var x -> lookup env x | Lambda(x,e) -> Lambda(x,subst env e) | App(e1,e2) -> (match eval e1 env, eval e2 env with | Lambda(x,e), v -> eval e ((x,v)::env))

Aha! Instead of doing the substitution when we reach a lambda, we could instead make a promise to finish the substitution if the lambda is ever applied. Lambda(x,subst env e) as code ==> Promise(subst,code) Then we have to modify App(_,_) to take care of the delayed substitution…

Closure-Based Semantics type value = Int_v of int | Closure_v of {env:env, body:var*exp} and env = (var * value) list let rec eval (e:exp) (env:env) : value = match e with | Int i -> Int_v i | Var x -> lookup env x | Lambda(x,e) -> Closure_v{env=env,body=(x,e)} | App(e1,e2) => (match eval e1 env, eval e2 env with | Closure_v{env=cenv,body=(x,e)}, v -> eval e ((x,v)::cenv))

Speeding up the Interpreter We have to do expensive string comparisons when looking up a variable: Var x => lookup env x where let rec lookup env x = match env with | ((y,v)::rest) -> if y = x then v else lookup rest | [] -> error “unbound variable”

DeBruijn Indices Instead of using strings to represent variables, let's use natural numbers: type exp = Int of int | Var of int | Lambda of exp | App of exp*exp The numbers will represent lexical depth: fun x -> fun y -> fun z -> x+y+z fun x 2 -> fun x 1 -> fun x 0 -> x 2 +x 1 +x 0 fun -> fun -> fun -> Var 2 + Var 1 + Var 0

Graphs as Trees fun x -> fun y -> fun z -> x+(y+z) fn x fn y fn z + + x y z

Graphs as Trees fun -> fun -> fun -> 2 + (1 + 0) fn

Converting: let rec cvt (e:exp) (env:var->int): D.exp = match e with | Int i -> D.Int i | Var x -> D.Var (env x) | App(e1,e2) -> D.App(cvt e1 env,cvt e2 env) | Lambda(x,e) => let new_env(y) = if y = x then 0 else (env y)+1 in Lambda(cvt e env)

New Interpreter: type value = Int_v of int | Closure_v of {env:env, body:exp} and env = value list let rec eval (e:exp) (env:env) : value = match e with | Int i -> Int_v i | Var n -> List.nth(env,n) | Lambda e -> Closure_v{env=env,body=e} | App(e1,e2) -> (match eval e1 env, eval e2 env with | Closure_v{env=cenv,body=e}, v -> eval e (v::cenv))

Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn

Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env

Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env

Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env

Alternative: type value = Int_v of int | Closure_v of {env:env, body:exp} and env = value vector let rec eval (e:exp) (env:env) : value = match e with | Int i -> Int_v i | Var n -> Vector.sub(env,n) | Lambda(e) -> Closure_v{env=env,body=e} | App(e1,e2) -> (match eval e1 env, eval e2 env with | Closure_v{env=cenv,body=e}, v -> eval e (vector_cons(v,cenv)))

Flat Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env

Flat Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env

Flat Environments (((fun -> fun -> fun -> 2 + (1 + 0)) 42) 37) 21 fn env 37 42

Schemeish Environments (lambda (x y z) (lambda (n m) (lambda (p) (+ n z)) fn 3 fn 2 fn 1 + 2,2 1,0 x,y,z n,m p x y z nil n m nxt p nxt