Tail Recursion: Factorial Begin at the beginning … Exec-time “Dynamic” Expressions (Syntax) Compile-time “Static” Values (Semantics) let rec fact n = if n<=0 then 1 else n * fact (n-1);; Types 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 meaningful type for the expr 3. ML evaluates expression to compute value • 2 Of the same “type” found in step 2 How does it execute? Tail recursion let rec fact n = if n<=0 then 1 else n * fact (n-1);; • Tail recursion: – recursion where all recursive calls are immediately followed by a return – in other words: not allowed to do anything between recursive call and return fac 3;; 3 Tail recursive factorial 4 Tail recursive factorial let fact x = let fact x = let rec helper x curr = if x <= 0 then curr else helper (x - 1) (x * curr) in helper x 1;; 5 6 1 How does it execute? Tail recursion • Tail recursion: let fact x = let rec helper x curr = if x <= 0 then curr else helper (x - 1) (x * curr) in helper x 1;; – for each recursive call, the value of the recursive call is immediately returned – in other words: not allowed to do anything between recursive call and return fact 3;; • Why do we care about tail recursion? – it turns out that tail recursion can be optimized into a simple loop 7 Compiler can optimize! let fact x = let rec helper x curr = Tail recursion summary • Tail recursive calls can be optimized as a jump fact(x) { curr := 1; while (1) { if x <= 0 if (x <= 0) then curr then { return curr } else helper (x - 1) (x * curr) else { x := x – 1; curr := (x * curr) } } in 8 • Part of the language specification of some languages (ie: you can count on the compiler to optimize tail recursive calls) helper x 1;; recursion! Loop! 9 Base Types 10 Base Type: int Expressions built from sub-expressions Types computed from types of sub-expressions Values computed from values of sub-expressions 2 Base Type: int 2 2; Base Type: float i i:int i )i e1)v1 e2)v2 e1+e2)v1+v2 2.0 +.3.0 5.0 e1:float e1:float e1 +. e2 e1+.e2:float 7.0 –. 4.0 3.0 e1 -. e2 e1:float e2:float e1)v1 e2)v2 e1-.e2:float e1-.e2)v1-.v2 (2.0 +. 3.0) /. (7.0 -. 4.0) 1.66.. e1 /. e2 e1:float e2:float e1)v1 e2)v2 e1/.e2:float e1/.e2)v1/.v2 2+3; 5 e1 + e2 e1:int e2:int e1+e2:int 7-4; 3 e1 - e2 e1:int e2:int e1-e2:int e1)v1 e2)v2 e1-e2)v1-v2 (2+3)*(7-4); 15 e1 * e2 e1:int e2:int e1 * e2:int e1)v1 e2)v2 e1*e2)v1*v2 Expressions built from sub-expressions Types computed from types of sub-expressions Values computed from values of sub-expressions Base Type: string “ab” ^ “cd” s “ab” “ab” “abcd” e1^e2 e 2.0 2.0 e1:string e2:string e1)v1 e2)v2 e1^e2:string e1^e2)v1^v2 Base Type: bool Expressions built from sub-expressions Types computed from types of sub-expressions Values computed from values of sub-expressions true true b b:bool b )b 2 < 3 true e1 < e2 e1:int e1:int e1 < e2:bool e1)v1 e2)v2 e1<e2 ) v1<v2 not(2<3) false not e e : bool not e : bool e ) v not e ) not v (“ab”=“cd”) false e1 = e2 e1:T e2:T e1=e2 : bool e1)v1 e2)v2 e1=e2 ) v1=v2 false e1 && e2 e1:bool e2:bool e1&&e2:bool e1)v1 e2)v2 e1&&e2) v1 && v2 not (2<3) && (“ab”=“cd”) Type Errors • Equality testing is built-in for all expr,values,types “pq” ^ 9; e1:string e2:string e1^e2:string (2 + “a”); e1:int e2:int e1 + e2 : int – but compared expressions must have same type • …except for ? – function values … why ? false e1 = e2 e1)v1 e2)v2 e1+.e2)v1+.v2 Base Type: bool s )s s:string Expressions built from sub-expressions Types computed from types of sub-expressions Values computed from values of sub-expressions (“ab”=“cd”) r )r r:float e1:T e2:T e1=e2 : bool e1)v1 e2)v2 e1=e2 ) v1=v2 • Expressions built from sub-expressions • Types computed from types of sub-expression • If a sub-expression is not well-typed then whole expression is not well-typed 0 * (2 + “a”); 3 Complex types: Tuples (2+2 , 7>8); (4,false) Complex types: Tuples (2+2 , 7>8); int * bool int * bool e1:T1 e2:T2 (e1,e2) : T1 * T2 Complex types: Tuples • Can be of any fixed size (9-3,“ab”^“cd”,7>8) (6, “abcd”, false) (int * string * bool) (4,false) e1)v1 e2)v2 (e1,e2)) (v1,v2) Complex types: Records {name=“sarah” ; age=31; pass=false} { name : string, age : int, pass : bool} Records are tuples with named elements… e1:T1 e2:T2 … en: Tn (e1,e2,…,en) : T1 * T2* …* Tn e1)v1 e2)v2 … en ) vn (e1,e2,…,en)) (v1,v2,…,vn) • Elements can have different types • Tuples can be nested in other tuples But wait… 31 int {age=31;name=“sarah”;pass=false}.age 31 int {age=31;name=“sarah”;pass=false}.pass false bool {name=“sarah”;age=31;pass=false}.age Complex types: Lists • All evaluation rules look like: e1)v1 e2)v2 e1 OP e2 ) v1 OP v2 [1+1;2+2;3+3;4+4] [2;4;6;8] int list [“a”;“b”;“c”^“d”]; [“a”;“b”;“cd”] string list [(1;“a”^“b”);(3+4,“c”)]; [(1,“ab”);(7,“c”)] (int*string) list [[1];[2;3];[4;5;6]]; [[1];[2;3];[4;5;6]]; (int list) list • Unbounded size • Can have lists of anything (e.g. lists of lists) 4 Complex types: Lists Complex types: Lists []:’a list [] [e1;e2;e3;…] [] ) [] e1:T e2: T e3: T … [e1;e2;e3;…] : T list e1)v1 e2)v2 e3)v3 [e1;e2;…] ) [v1;v2;…] All elements have the same type [1; “pq”] Complex types: list ..construct Complex types: list ..construct Cons “operator” 1::[2;3] [1;2;3] int list e1:T e2: T list e1::e2 : T list e1)v1 e2 ) v2 e1::e2 ) v1::v2 1::[“b”; “cd”]; Can only “cons” element to a list of same type Complex types: list …construct Complex types: list … deconstruct Append “operator” Reading the elements of a list: • Two “operators”: hd (head) and tl (tail) [1;2]@[3;4] [1;2;3;4] int list e1:T list e2: T list e1@e2 : T list e1)v1 e2 ) v2 e1@e2 ) v1@v2 1@[“b”; “cd”]; [1]@[“b”; “cd”]; Can only append lists of the same type [1;2;3;4;5] hd [1;2;3;4;5] 1 tl [1;2;3;4;5] int [“a”;“b”; “cd”] hd [“a”;“b”;“cd”] “a” string [2;3;4;5] int list tl [“a”;“b”;“cd”] [“b”; “cd”] string list [(1,“a”);(7,“c”)] hd [(1,“a”);(7,“c”)](1,”a”) tl [(1,“a”);(7,“c”)] [(7; “c”)] Int * string [[];[1;2;3];[4;5]] (int * string) list hd [[];[1;2;3];4;5] 1 tl [[];[1;2;3];4;5] [2;3;4;5] int list int list list 5 List: Heads and Tails List: Heads and Tails Head Tail e :T list hd e : T e )v1::v2 hd e ) v1 e :T list tl e : T list e ) v1::v2 tl e ) v2 (hd [[];[1;2;3]]) = (hd [[];[“a”]]) int list Exec-time “Dynamic” Expressions (Syntax) Compile-time “Static” Values (Semantics) Types 1. Programmer enters expression 2. ML checks if expression is “well-typed” • • • • Integers: +,-,* floats: +,-,* Booleans: =,<, andalso, orelse, not Strings: ^ • Tuples, Records: #i Using a precise set of rules, ML tries to find a unique type for the expression meaningful type for the expr 3. ML evaluates expression to compute value • string list Recap Recap • e1:T e2:T e1=e2 : bool – Fixed number of values, of different types • Lists: ::,@,hd,tl,null – Unbounded number of values, of same type Of the same “type” found in step 2 If-then-else expressions If-then-else expressions 5 if (1 < 2) then 5 else 10 if (1 < 2) then [“ab”,“cd”] else [“x”] [“ab”,“cd”] int if (1 < 2) then [“ab”,“cd”] else [“x”] [“ab”,“cd”] string list If-then-else is also an expression! Can use any expression in then, else branch 5 if (1 < 2) then 5 else 10 int string list If-then-else is also an expression! Can use any expression in then, else branch if e1 then e2 else e3 if e1 then e2 else e3 e1 : bool e2: T e3: T if e1 then e2 else e3 : T e1 ) true e2 ) v2 if e1 then e2 else e3 ) v2 e1 ) false e3 ) v3 if e1 then e2 else e3 ) v3 6 If-then-else expressions If-then-else expressions if (1 < 2) then [1;2] else 5 e1 if false then [1;2] else 5 • then-subexp, else-subexp must have same type! – …which is the type of resulting expression e1 : bool e2: T e3: T if e1 then e2 else e3 : T : bool e2: T e3: T if e1 then e2 else e3 : T • Then-subexp, Else-subexp must have same type! – Equals type of resulting expression if 1>2 then [1,2] else [] [] if 1<2 then [] else [“a”] [] int list string list (if 1>2 then [1,2] else [])=(if 1<2 then [] else [“a”]) Next: Variables Variables and Bindings Q: How to use variables in ML ? Q: How to “assign” to a variable ? # let x = 2+2;; val x : int = 4 let x = e;; “Bind the value of expression to the variable Variables and Bindings # let val x # let val y # let val z x : y : z : x” Environments (“Phone Book”) How ML deals with variables • Variables = “names” • Values = “phone number” = 2+2;; int = 4 = x * x * x;; int = 64 = [x;y;x+y];; int list = [4;64;68] Later declared expressions can use e x – Most recent “bound” value used for evaluation x y z x 4 : int 64 : int [4;64;68] : int list 8 : int Sounds like C/Java ? NO! 7 Environments and Evaluation ML begins in a “top-level” environment • Some names bound Environments “Phone book” • Variables = “names” • Values = “phone number” let x = e;; ML program = Sequence of variable bindings Program evaluated by evaluating bindings in order 1. Evaluate expr e in current env to get value v : t 2. Extend env to bind x to v : t (Repeat with next binding) 1. Evaluate: Find and use most recent value of variable 2. Extend: Add new binding at end of “phone book” Environments Example # let x = 2+2;; val x : int = 4 4 : int x # let y = x * x * x;; val y : int = 64 # let x = x + x ;; val x : int = 8 # let x = 2+2;; val x : int = 4 4 : int 64 : int [4;64;68] : int list x y z New binding! How is this different from C/Java’s “store” ? 4 : int 64 : int x y # let z = [x;y;x+y];; val z : int list = [4;64;68] 1. Evaluate: Use most recent bound value of var 2. Extend: Add new binding at end x y z x 4 : int 64 : int [4;64;68] : int list 8 : int # let f = fun y -> x + y; val f : int -> int = fn # let x = x + x ; val x : int = 8 # f 0; val it : int = 4 4 : int x 4 : int fn <code, x f >: int->int New binding: • No change or mutation • Old binding frozen in f Environments Environments 1. Evaluate: Use most recent bound value of var 2. Extend: Add new binding at end 1. Evaluate: Use most recent bound value of var 2. Extend: Add new binding at end How is this different from C/Java’s “store” ? How is this different from C/Java’s “store” ? # let x = 2+2;; val x : int = 4 # let f = fun y -> x + y; val f : int -> int = fn # let x = x + x ; val x : int = 8 # f 0; val it : int = 4 # let x = 2+2;; val x : int = 4 4 : int x # let f = fun y -> x + y; val f : int -> int = fn 4 : int fn <code, x f >: int->int # let x = x + x ; val x : int = 8 x 4 : int f fn <code, x 8 : int >: int->int # f 0; val it : int = 4 Binding used to eval (f …) x 4 : int f fn <code, x 8 : int >: int->int Binding for subsequent x 8 Cannot change the world Cannot change the world Cannot “assign” to variables • Can extend the env by adding a fresh binding • Does not affect previous uses of variable Q: Why is this a good thing ? A: Function behavior frozen at declaration Environment at fun declaration frozen inside fun “value” • Frozen env used to evaluate application (f …) Q: Why is this a good thing ? Binding used to eval (f …) # let x = 2+2;; val x : int = 4 # let f = fun y -> x + y;; val f : int -> int = fn # let x = x + x ;; val x : int = 8; # f 0;; val it : int = 4 x 4 : int f fn <code, x 8 : int >: int->int • Nothing entered afterwards affects function • Same inputs always produce same outputs – Localizes debugging – Localizes reasoning about the program – No “sharing” means no evil aliasing Binding for subsequent x Examples of no sharing Recap: Environments Remember: No addresses, no sharing. • Each variable is bound to a “fresh instance” of a value “Phone book” • Variables = “names” • Values = “phone number” Tuples, Lists … • Efficient implementation without sharing ? • There is sharing and pointers but hidden from you • Compiler’s job is to optimize code • Efficiently implement these “no-sharing” semantics • Your job is to use the simplified semantics • Write correct, cleaner, readable, extendable systems 1. Evaluate: Find and use most recent value of variable 2. Extend: let x = e ;; Add new binding at end of “phone book” Functions Next: Functions expr Functions are values, can bind using let Values Expressions Types let fname = fun x -> e ;; Problem: Can’t define recursive functions ! • fname is bound after computing rhs value • no (or “old”) binding for occurences of fname inside e let rec fname x = e ;; Occurences of fname inside e bound to “this” definition let rec fac x = if x<=1 then 1 else x*fac (x-1) 9 Functions Type Functions Type e1 : T2 -> T e1 e2 : T Functions Values e2: T2 Functions Two questions about function values: Two questions about function values: What is the value: What is the value: 1. … of a function ? 1. … of a function ? 2. … of a function “application” (call) ? (e1 e2) 2. … of a function “application” (call) ? Values of functions: Closures • “Body” expression not evaluated until application – but type-checking takes place at compile time – i.e. when function is defined Values (e1 e2) Values of function application Application: fancy word for “call” (e1 e2) • “apply” the argument e2 to the (function) e1 • Function value = Application Value: 1. Evaluate e1 in current env to get (function) v1 – <code + environment at definition> – “closure” # let x = 2+2;; val x : int = 4 # let f = fun y -> x + y;; val f : int -> int = fn # let x = x + x ;; val x : int = 8 # f 0;; val it : int = 4 Binding used to eval (f …) x 4 : int f fn <code, x 8 : int >: int->int – – v1 is code + env code is (formal x + body e) , env is E 2. Evaluate e2 in current env to get (argument) v2 3. Evaluate body e in env E extended by binding x to v2 Binding for subsequent x 10 Example 1 Example 1 let x = 1;; let f y = x + y;; let x = 2;; let y = 3;; f (x + y);; let x = 1;; let f y = x + y;; let x = 2;; let y = 3;; f (x + y);; Example 2 Example 2 let x = 1;; let f y = let x = 2 in fun z -> x + y + z ;; let x = 1;; let f y = let x = 2 in fun z -> x + y + z ;; let x = 100;; let g = (f 4);; let y = 100;; (g 1);; let x = 100;; let g = (f 4);; let y = 100;; (g 1);; Example 3 let f g = let x = 0 in g 2 ;; let x = 100;; let h y = x + y;; f h;; Static/Lexical Scoping • For each occurrence of a variable, – Unique place in program text where variable defined – Most recent binding in environment • Static/Lexical: Determined from the program text – Without executing the programy • Very useful for readability, debugging: – Don’t have to figure out “where” a variable got assigned – Unique, statically known definition for each occurrence 11 Alternative: dynamic scoping let x = 100 let f y = x + y let g x = f 0 let z = g 0 (* value of z? *) 12
© Copyright 2024