CSE 130: Spring 2010 Programming Languages Lecture 3: Epressions and Types Ranjit Jhala UC San Diego A Problem fun -> +1 Can functions only have a single parameter? A Solution: Simultaneous Binding Parameter (formal) Body Epr fun (,y) -> <y; (int * int) -> bool Can functions only have a single parameter? Another Solution Parameter (formal) Body Epr fun -> fun y-> <y; int -> (int -> bool) Whoa! A function can return a function # let lt = fun -> fn y -> < y ; val lt : int -> int -> bool = fn # let is5lt = lt 5; val is5lt : int -> bool = fn; # is5lt 10; val it : bool = true; # is5lt 2; val it : bool = false;
And how about Parameter (formal) Body Epr fun f -> fun -> not(f ); ( a ->bool) -> ( a -> bool) A function can also take a function argument # let neg = fun f -> fun -> not (f ); val lt : int -> int -> bool = fn # let is5gte = neg is5lt; val is5gte : int -> bool = fn # is5gte 10; val it : bool = false; # is5gte 2; val it : bool = true; (* odd, even *) A shorthand for function binding # let neg = fun f -> fun -> not (f ); # let neg f = not (f ); val neg : int -> int -> bool = fn # let is5gte = neg is5lt; val is5gte : int -> bool = fn; # is5gte 10; val it : bool = false; # is5gte 2; val it : bool = true; Put it together: a filter function If arg matches this pattern then use this Body Epr - let rec filter f l = match l with [] -> [] (h::t)-> if f h then h::(filter f t) else (filter f t) val filter : ( a->bool)-> a list-> a lisi) = fn # let list1 = [1;31;12;4;7;2;10] # filter is5lt list1 val it : int list = [31;12;7;10] # filter is5gte list1 val it : int list = [1;4;2] # filter even list1 val it : int list = [12;4;2;10] Put it together: a partition function # let partition f l = (filter f l, filter (neg f) l); val partition :( a->bool)-> a list-> a lisi * a list = fn # let list1 = [1,31,12,4,7,2,10]; - # partition is5lt list1 ; val it : (int list * int list) = ([31,12,7,10],[1,2,10] # partition even list1; val it : (int list * int list) = ([12,4,2,10],[1,31,7])
A little trick # 2 <= 3 val it : bool = true # ba <= ab val it : bool = false # let lt = (<) val it : a -> a -> bool = fn # lt 2 3 val it : bool = true; # lt ba ab val it : bool = false; Put it together: a quicksort function let rec sort s = match s with [] -> [] (h::t) -> let (l,r) = partition ((<) h) t in (sort l)@(h::(sort r)) # let is5lt = lt 5; val is5lt : int -> bool = fn; # is5lt 10; val it : bool = true; # is5lt 2; val it : bool = false; Now, lets begin at the beginning News PA 1 due 5pm tomorrow PA 2 out tomorrow Begin at the beginning Epressions (Synta) Compile-time Static Eec-time Dynamic Types Values (Semantics) 1. Programmer enters epression 2. L checks if epression is well-typed Using a precise set of rules, L tries to find a unique type for the epression meaningful type for the epr 3. L evaluates epression to compute value Of the same type found in step 2
Base Type: int 2; 2 i i:int i i Base Type: float 2.0 2.0 e r:float r r 2+3; 5 e1 + e2 e 1 :int e 2 :int e 1 +e 2 :int e1 v1 e2 v2 e1+e2 v1+v2 2.0 +.3.0 5.0 e1 +. e2 e 1 :float e 1 :float e 1 +.e 2 :float e1 v1 e2 v2 e1+.e2 v1+.v2 7-4; 3 e1 - e2 e 1 :int e 2 :int e 1 -e 2 :int e1 v1 e2 v2 e1-e2 v1-v2 7.0. 4.0 3.0 e1 -. e2 e 1:float e 2 :float e 1 -.e 2 :float e1 v1 e2 v2 e1-.e2 v1-.v2 (2+3)*(7-4); 15 e1 * e2 e 1 :int e 2 :int e1 v1 e2 v2 e 1 * e 2 :int e1*e2 v1*v2 (2.0 +. 3.0) /. (7.0 -. 4.0) 1.66.. e1 /. e2 e 1:float e 2 :float e1 v1 e2 v2 e 1 /.e 2 :float 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: string ab ab s s:string s s Base Type: bool true true b b:bool b b 2 < 3 true e1 < e2 e 1 :T e 2 :T e 1 <e 2 : bool e1 v1 e2 v2 e1<e2 v1<v2 ab ^ cd abcd e1^e2 e 1 :string e 2 :string e 1^e 2 :string e1 v1 e2 v2 e1^e2 v1^v2 not(2<3) false not e ( ab = cd ) false e1 = e2 e : bool not e : bool e v not e not v e 1 :TT e 2 :T e1 v1 e2 v2 e 1 =e 2 : bool e1=e2 v1=v2 Epressions built from sub-epressions Types computed from types of sub-epressions Values computed from values of sub-epressions not (2<3) e false e1 && e2 1 :bool e 2 :bool e1 v1 e2 v2 && e 1 &&e 2 :bool e1&&e2 v1 && v2 ( ab = cd )
Base Type: bool Equality testing built-in in for all epr, values, types but compared epressions must have same type ecept for? Type Errors pq ^ 9; (2 + a ); e 1 :string e 2 :string e 1^e 2 :stringi e 1 :int e 2 :int e 1 + e 2 : int ( ab = cd ) false e1 = e2 e 1 :TT e 2 :T e1 v1 e2 v2 e 1 =e 2 : bool e1=e2 v1=v2 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 0 * (2 + a ); Comple types: Tuples (2+2, 7>8); (4,false) int * bool Comple types: Tuples Can be of any fied size (9-3, ab ^ cd,7>8) (int * string * bool) (6, abcd, false) e1:t1 e2:t2 e1 v1 e2 v2 (e 1,e 2 ) : T1 * T2 (e1,e2) (v1,v2) 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
Comple types: Records Comple types: Lists {name= ranjit ; age=31; pass=false} { name : string, age : int, pass : bool} [1+1;2+2;3+3;4+4] [2;4;6;8] int list [ a ; b ; c ^ d ]; [ a ; b ; cd ] string list Records are tuples with named elements [(1; a ^ b );(3+4, c )]; [(1, ab );(7, c )] (int*string) list {name= ranjit ;age=31;pass=false}.age 31 int [[1];[2;3];[4;5;6]]; [[1];[2;3];[4;5;6]]; (int list) list {age=31;name= ranjit ;pass=false}.age 31 int Unbounded size {age=31;name= ranjit ;pass=false}.pass false bool Can have lists of anything (e.g. lists of lists) Comple types: Lists Comple types: list..construct [] [e1;e2;e3; ] []: a list [] [] e1:t e2: T e3: T e1 v1 e2 v2 e3 v3 [e1;e2;e3; ] : T list [e1;e2; ] [v1;v2; ] 1 2 Cons operator e1:t e2: T list e1::e2 : T list 1::[2;3] [1;2;3] int list e1 v1 e2 v2 e1::e2 v1::v2 All elements have the same type [1; pq ] 1::[ b ; cd ]; Can only cons element to a list of same type
Comple types: list construct Append operator [1;2]@[3;4] [1;2;3;4] int list Comple types: list deconstruct Reading the elements of a list: Two operators : hd (head) and tl (tail) e1:t list e2: T list e1@e2 : T list e1 v1 e2 v2 e1@e2 v1@v2 [1;2;3;4;5] hd [1;2;3;4;5] 1 int tl [1;2;3;4;5] [2;3;4;5] int list 1@[ b ; cd ]; [ a ; b ; cd ] hd [ a ; b ; cd ] a string tl [ a ; b ; cd ] [ b ; cd ] string list [1]@[ b ; cd ]; Can only append lists of the same type [(1, a );(7, c )] hd [(1, a );(7, c )] 1 int [[];[1;2;3];[4;5]] hd [[];[1;2;3];4;5] 1 int list tl [(1, a );(7, c )] [(7; c ] (int * string) list tl [[];[1;2;3];4;5] [2;3;4;5] int list list List: Heads and Tails Recap Head e :T list hd e : T e v1::v2 hd e v1 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]])[1 = (hd [[];[ a ]])[ int list e 1 :T e 2 :T e 1 =e 2 : bool string list 1. Programmer enters epression 2. L checks if epression is well-typed Using a precise set of rules, L tries to find a unique type for the epression meaningful type for the epr 3. L evaluates epression to compute value Of the same type found in step 2
Recap Integers: + -* floats: +. -. *. Booleans: =,<, &&,, not Strings: ^ Tuples (Records) Fied number of values, of dff different types Lists: ::,@,hd,tl,null Unbounded number of values, of same type If-then-else else epressions if (1 < 2) then 5 else 10 5 if (1 < 2) then [ ab, cd ] else [ ] If-then-else is also an epression! int [ ab, cd ] string ti list lit 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 e1 true e2 v2 if e1 then e2 else e3 v2 e1 false e2 v3 if e1 then e2 else e3 v3 If-then-else 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 If-then-else else epressions 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 [] [] int list if 1<2 then [] else [ a ] [] string list (if 1>2 then [1,2] else [])=(if 1<2 then [] else [ a ])
Net: Variables Variables and Bindings Q: How to use variables in L? 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 : int list = [4;64;68] Later declared epressions can use ost recent bound value used for evaluation Sounds like C/Java? NO! Environments ( Phone Book ) How L deals with variables Variables = names Values = phone number y z 4 : int 64 : int [4;64;68] : int list 8 : int
Environments and Evaluation L begins in a top-level environment Some names bound let = e L 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) Environments 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 Eample # let = 2+2 val : int = 4 # let y = * * val y : int = 64 y 4 : int 4 : int 64 : int # let z = [;y;+y] val z : int list = [4;64;68] 4 : int # let = + val : int = 8 New binding! y z y z 64 : int [4;64;68] : int list 4 : int 64 : int [4;64;68] : int list 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 4 : int # let f = fun y -> + y; val f : int -> int = fn 4 : int f fn <code, >: int->int # let = + ; val : int = 8 # f 0; val it : int = 4 New binding: No change or mutation Old binding frozen in f
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? 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 4 : int # let f = fun y -> + y; val f : int -> int = fn 4 : int f fn <code, >: int->int # let = + ; val : int = 8; 4 : int # f 0; f fn <code, >: int->int val it : int = 4 8 : int # let = 2+2; val : int = 4 # let f = fun y -> + y val f : int -> int = fn Binding used to eval (f ) # let = + ; val : int = 8 4 : int f fn <code, >: int->int # f 0; 8 : int val it : int = 4 Binding for subsequent 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 ) Q: Why is this a good thing? # let = 2+2 Binding used to eval (f ) val : int = 4 # let f = fun y -> + y val f : int -> int = fn # let = + val : int = 8; # f 0 val it : int = 4 4 : int f fn <code,, >: int->int 8 : int Binding for subsequent 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 i
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 Function bindings Functions are values, can bind using val 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 id e let rec fname = e Occurences of fname inside e bound to this definition let rec fac = if <=1 then 1 else *fac (-1) Local bindings So far: global bindings (Remain till a re-binding) Net: local variables Useful inside functions Avoid repeating computations ake functions more readable Local bindings let = e1 in e2 Let-in is an epression! Evaluating let-in in env E: 1. Evaluate epr e1 in env E to get value v : t 2. Use etended E [ a v : t] to evaluate e2
Local bindings Evaluating let-in in env E: 1. Evaluate epr e1 in env E to get value v : t 2. Use etended E [ a v : t] to evaluate e2 let in = 10 * 10 : int Let-in is an epression! Evaluating let-in in env E: 1. Evaluate epr e1 in env E to get value v : t 2. Use etended E [ a v : t] to evaluate e2 let y = let = 10 in * y 100 : int 10 : int Nested bindings Evaluating let-in in env E: 1. Evaluate epr e1 in env E to get value v : t 2. Use etended E [ a v : t] to evaluate e2 let = 10 in (let y = 20 in * y) + 10 : int y 10 : int 10 : int 20 : int Nested bindings let = 10 in let in y = 20 * y let = 10 in let y = 20 in * y Correct Formatting
Eample Nested function bindings let rec filter (f,l) = if l = [] then [] else let h = hd l in let t = filter (f, tl l) in if (f h) then h::t else t let a = 20 let f = let y = 10 in let g z = y + z in a + (g ) f 0; Env frozen with function Used to evaluate fun application Values in application are those frozen in env at definition Recap Recap Variables are names for values Environment: dictionary/phonebook ost recent binding used Entries never changed, new entries added Build comple epressions with local bindings let-in epression The let-binding is visible (in scope) inside in-epression Elsewhere the binding is not visible Environment frozen at fun definition Re-binding vars cannot change function behavior Same I/O behavior at every call
Static/Leical Scoping Net: Functions For each occurrence of a variable, there is a unique place in program tet where the variable was defined ost recent binding in environment Static/Leical: Determined from the program tet Without eecuting the programy Epressions Types Values Very useful for readability, debugging: Don t have to figure out where a variable got assigned Unique, statically known definition for each occurrence Q: What s the value of a function?