Begin at the beginning Epressions (Synta) Compile-time Static Eec-time Dynamic Types Values (Semantics) 1. Programmer enters epression 2. ML checks if epression is well-typed Using a precise set of rules, ML tries to find a unique type for the epression meaningful type for the epr 3. ML evaluates epression to compute value Of the same type found in step 2 Side note: Tail Recursion let fact n = if n <= 0 then 1 else n * fact (n 1) Tail recursion: for each recursive call, the value of the recursive call is immediately returned Side note: Tail Recursion Base Types let rec fact n = if n <= 0 then 1 else n * fact (n 1) let rec fact_tr n res = if n <= 0 then res else fact_tr (n 1) (n*res) let fact n = fact_tr n 1 Base Type: int Base Type: int 2; 2 i i:int i ) i 2+3; 5 e1 + e2 e 1 :int e 2 :int e 1 +e 2 :int e1+e2)v1+v2 7-4; 3 e1 - e2 e 1 :int e 2 :int e 1 -e 2 :int e1-e2)v1-v2 (2+3)*(7-4); 15 e1 * e2 e 1 :int e 2 :int e 1 * e 2 :int e1*e2)v1*v2 Epressions built from sub-epressions Types computed from types of sub-epressions Values computed from values of sub-epressions Epressions built from sub-epressions Types computed from types of sub-epressions Values computed from values of sub-epressions 1
Base Type: float 2.0 2.0 e r:float r ) r Base Type: string ab ab s s:string s ) s 2.0 +.3.0 5.0 e1 +. e2 e 1 :float e 1 :float e 1 +.e 2 :float e1+.e2)v1+.v2 7.0. 4.0 3.0 (2.0 +. 3.0) /. (7.0 -. 4.0) 1.66.. e1 -. e2 e1 /. e2 e 1 :float e 2 :float e 1 -.e 2 :float e 1 :float e 2 :float e 1 /.e 2 :float e1-.e2)v1-.v2 e1/.e2)v1/.v2 ab ^ cd abcd e1^e2 e 1 :string e 2 :string e 1^e 2 :string e1^e2)v1^v2 Epressions built from sub-epressions Types computed from types of sub-epressions Values computed from values of sub-epressions Epressions built from sub-epressions Types computed from types of sub-epressions Values computed from values of sub-epressions Base Type: bool true true b b:bool b ) b 2 < 3 true not(2<3) ( ab = cd ) false false e1 < e2 not e e1 = e2 e 1 :int e 1 :int e 1 < e 2 :bool e : bool not e : bool e 1 :T e 2 :T e 1 =e 2 : bool e1<e2 ) v1<v2 e ) v not e ) not v e1=e2 ) v1=v2 Base Type: bool Equality testing is built-in for all epr,values,types but compared epressions must have same type ecept for? function values why? ( ab = cd ) false e1 = e2 e 1 :T e 2 :T e 1 =e 2 : bool e1=e2 ) v1=v2 not (2<3) && ( ab = cd ) false e1 && e2 e 1 :bool e 2 :bool e 1 &&e 2 :bool e1&&e2) v1 && v2 Type Errors pq ^ 9; (2 + a ); e 1 :string e 2 :string e 1^e 2 :string e 1 :int e 2 :int e 1 + e 2 : int Epressions built from sub-epressions Types computed from types of sub-epression If a sub-epression is not well-typed then whole epression is not well-typed Comple types: Tuples (2+2, 7>8); (4,false) int * bool 0 * (2 + a ); 2
Comple types: Tuples Comple types: Tuples Can be of any fied size (2+2, 7>8); (4,false) int * bool (9-3, ab ^ cd,7>8) (6, abcd, false) (int * string * bool) e1:t1 e2:t2 (e 1,e 2 ) : T1 * T2 (e1,e2)) (v1,v2) e1:t1 e2:t2 en: Tn (e1,e2,,en) : T1 * T2* * Tn en ) vn (e1,e2,,en)) (v1,v2,,vn) Elements can have different types Tuples can be nested in other tuples Comple types: Records {name= sarah ; age=31; pass=false} { name : string, age : int, pass : bool} Records are tuples with named elements But wait All evaluation rules look like: e1 OP e2 ) v1 OP v2 {name= sarah ;age=31;pass=false}.age 31 int {age=31;name= sarah ;pass=false}.age 31 int {age=31;name= sarah ;pass=false}.pass false bool Comple types: Lists Comple types: Lists [1+1;2+2;3+3;4+4] [2;4;6;8] [ 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]]; () list Unbounded size Can have lists of anything (e.g. lists of lists) 3
Comple types: Lists Comple types: list..construct [] []: a list [] ) [] [e1;e2;e3; ] e1:t e2: T e3: T [e1;e2;e3; ] : T list e3)v3 [e1;e2; ] ) [v1;v2; ] All elements have the same type [1; pq ] Comple types: list..construct Comple types: list construct Cons operator 1::[2;3] [1;2;3] Append operator [1;2]@[3;4] [1;2;3;4] e1:t e2: T list e1::e2 : T list e1)v1 e2 ) v2 e1::e2 ) v1::v2 e1:t list 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 1@[ b ; cd ]; [1]@[ b ; cd ]; Can only append lists of the same type Comple types: list deconstruct List: Heads and Tails Reading the elements of a list: Two operators : hd (head) and tl (tail) [1;2;3;4;5] hd [1;2;3;4;5] 1 int tl [1;2;3;4;5] [2;3;4;5] [ a ; b ; cd ] hd [ a ; b ; cd ] a tl [ a ; b ; cd ] [ b ; cd ] string string list [(1, a );(7, c )] hd [(1, a );(7, c )] (1, a ) Int * string tl [(1, a );(7, c )] [(7; c )] (int * string) list [[];[1;2;3];[4;5]] hd [[];[1;2;3];4;5] 1 tl [[];[1;2;3];4;5] [2;3;4;5] list 4
List: Heads and Tails Head e :T list hd e : T e )v1::v2 hd e ) v1 Recap Epressions (Synta) Eec-time Dynamic Values (Semantics) Tail e :T list tl e : T list e ) v1::v2 tl e ) v2 Compile-time Static Types (hd [[];[1;2;3]]) = (hd [[];[ a ]]) e 1 :T e 2 :T e 1 =e 2 : bool string list 1. Programmer enters epression 2. ML checks if epression is well-typed Using a precise set of rules, ML tries to find a unique type for the epression meaningful type for the epr 3. ML evaluates epression to compute value Of the same type found in step 2 Recap Integers: +,-,* floats: +,-,* Booleans: =,<, andalso, orelse, not Strings: ^ Tuples, Records: #i Fied number of values, of different types Lists: ::,@,hd,tl,null Unbounded number of values, of same type If-then-else epressions if (1 < 2) then 5 else 10 5 if (1 < 2) then [ ab, cd ] else [ ] If-then-else is also an epression! Can use any epression in then, else branch if e1 then e2 else e3 int [ ab, cd ] string list If-then-else epressions if (1 < 2) then 5 else 10 5 if (1 < 2) then [ ab, cd ] else [ ] If-then-else is also an epression! Can use any epression in then, else branch if e1 then e2 else e3 e1 : bool e2: T e3: T if e1 then e2 else e3 : T int [ ab, cd ] string list If-then-else epressions if (1 < 2) then [1;2] else 5 if false then [1;2] else 5 then-subep, else-subep must have same type! which is the type of resulting epression 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 5
If-then-else epressions Net: Variables e1 : bool e2: T e3: T if e1 then e2 else e3 : T Then-subep, Else-subep must have same type! Equals type of resulting epression if 1>2 then [1,2] else [] [] if 1<2 then [] else [ a ] [] string list (if 1>2 then [1,2] else [])=(if 1<2 then [] else [ a ]) Variables and Bindings Q: How to use variables in ML? Q: How to assign to a variable? # let = 2+2;; val : int = 4 let = e;; Bind the value of epression e to the variable Variables and Bindings # let = 2+2;; val : int = 4 # let y = * * ;; val y : int = 64 # let z = [;y;+y];; val z : = [4;64;68] Later declared epressions can use Most recent bound value used for evaluation Sounds like C/Java? NO! Environments ( Phone Book ) How ML deals with variables Variables = names Values = phone number Environments and Evaluation ML begins in a top-level environment Some names bound let = e;; y z 6 [4;64;68] : 8 : int ML program = Sequence of variable bindings Program evaluated by evaluating bindings in order 1. Evaluate epr e in current env to get value v : t 2. Etend env to bind to v : t (Repeat with net binding) 6
Environments Eample Phone book Variables = names Values = phone number 1. Evaluate: Find and use most recent value of variable 2. Etend: Add new binding at end of phone book # let = 2+2;; val : int = 4 # let y = * * ;; val y : int = 64 # let z = [;y;+y];; val z : = [4;64;68] # let = + ;; val : int = 8 New binding! y y z y z 6 6 [4;64;68] : 6 [4;64;68] : 8 : int Environments Environments 1. Evaluate: Use most recent bound value of var 2. Etend: Add new binding at end How is this different from C/Java s store? 1. Evaluate: Use most recent bound value of var 2. Etend: Add new binding at end How is this different from C/Java s store? # let = 2+2;; val : int = 4 # let = 2+2;; val : int = 4 # let f = fun y -> + y; val f : int -> int = fn # let = + ; val : int = 8 # f 0; val it : int = 4 New binding: No change or mutation Old binding frozen in f # let f = fun y -> + y; val f : int -> int = fn # let = + ; val : int = 8 # f 0; val it : int = 4 8 : int Environments 1. Evaluate: Use most recent bound value of var 2. Etend: Add new binding at end How is this different from C/Java s store? # let = 2+2;; val : int = 4 Cannot change the world Cannot assign to variables Can etend the env by adding a fresh binding Does not affect previous uses of variable Environment at fun declaration frozen inside fun value Frozen env used to evaluate application (f ) # let f = fun y -> + y; val f : int -> int = fn # let = + ; val : int = 8 # f 0; val it : int = 4 Binding used to eval (f ) 8 : int Binding for subsequent Q: Why is this a good thing? # let = 2+2;; val : int = 4 # let f = fun y -> + y;; val f : int -> int = fn # let = + ;; val : int = 8; # f 0;; val it : int = 4 Binding used to eval (f ) 8 : int Binding for subsequent 7
Cannot change the world Q: Why is this a good thing? A: Function behavior frozen at declaration Nothing entered afterwards affects function Same inputs always produce same outputs Localizes debugging Localizes reasoning about the program No sharing means no evil aliasing Eamples of no sharing Remember: No addresses, no sharing. Each variable is bound to a fresh instance of a value 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, etendable systems Recap: Environments Net: Functions Phone book Variables = names Values = phone number Epressions Values 1. Evaluate: Find and use most recent value of variable Types 2. Etend: let = e ;; Add new binding at end of phone book Functions epr Functions Type Functions are values, can bind using let let fname = fun -> 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 = e ;; Occurences of fname inside e bound to this definition let rec fac = if <=1 then 1 else *fac (-1) 8
Functions Type Functions Values Two questions about function values: What is the value: e1 : T2 -> T e1 e2 : T e2: T2 1. of a function? 2. of a function application (call)? (e1 e2) Functions Values Values of functions: Closures Two questions about function values: What is the value: 1. of a function? 2. of a function application (call)? (e1 e2) Body epression not evaluated until application but type-checking takes place at compile time i.e. when function is defined Function value = <code + environment at definition> closure # let = 2+2;; val : int = 4 # let f = fun y -> + y;; val f : int -> int = fn # let = + ;; val : int = 8 # f 0;; val it : int = 4 Binding used to eval (f ) 8 : int Binding for subsequent Values of function application Eample 1 Application: fancy word for call (e1 e2) apply the argument e2 to the (function) e1 Application Value: 1. Evaluate e1 in current env to get (function) v1 v1 is code + env code is (formal + body e), env is E 2. Evaluate e2 in current env to get (argument) v2 3. Evaluate body e in env E etended by binding to v2 let = 1;; let f y = + y;; let = 2;; let y = 3;; f ( + y);; 9
Eample 1 let = 1;; let f y = + y;; let = 2;; let y = 3;; f ( + y);; Eample 2 let = 1;; let f y = let = 2 in fun z -> + y + z ;; let = 100;; let g =(f 4);; let y = 100;; (g 1);; Eample 2 let = 1;; let f y = let = 2 in fun z -> + y + z ;; let = 100;; let g = (f 4);; let y = 100;; (g 1);; Eample 3 let f g = let = 0 in g 2 ;; let = 100;; let h y = + y;; f h;; Static/Leical Scoping For each occurrence of a variable, Unique place in program tet where variable defined Most recent binding in environment Static/Leical: Determined from the program tet Without eecuting 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 Alternative: dynamic scoping let = 100 let f y = + y let g = f 0 let z = g 0 (* value of z? *) 10