CSE 130: Spring 2010
Programming Languages
Lecture 3: E i d TExpressions and Types
Ranjit JhalaUC San Diego
A ProblemA Problem
fun x -> x+1;;fun x > x+1;;
Can functions only have Can functions only have a single parameter ?
A Solution: Simultaneous BindingA Solution: Simultaneous Binding
Parameter Body
fun (x,y) -> x<y;
(formal)BodyExpr
fun (x,y) > x<y;
(int * int) -> bool
Can functions only have Can functions only have a single parameter ?
Another SolutionAnother SolutionParameter
(formal)BodyExpr
fun x -> fun y-> x<y;
(formal) Expr
int -> (int -> bool)
Whoa! A function can return a functionWhoa! A function can return a function# let lt = fun x -> fn y -> x < y ; val lt : int -> int -> bool = fnval 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 aboutAnd how about…Parameter
(formal)BodyExpr
fun f -> fun x -> not(f x);
(formal) Expr
(’a ->bool) -> (’a -> bool)
A function can also take a function argumentA function can also take a function argument# let neg = fun f -> fun x -> not (f x);val lt : int -> int -> bool = fn # let is5gte = neg is5lt;val is5gte : int -> bool = fn# is5gte 10; val it : bool = false;val it : bool false;# is5gte 2;val it : bool = true;(*…odd, even …*)
A shorthand for function bindingA shorthand for function binding
## let neg = fun f -> fun x -> not (f x);…# let neg f x = not (f x);val neg : int -> int -> bool = fn
# let is5gte = neg is5lt;# let is5gte neg is5lt;val is5gte : int -> bool = fn;# is5gte 10; val it : bool = false;val it : bool = false;# is5gte 2;val it : bool = true;
Put it together: a “filter” functionPut it together: a filter functionIf arg “matches”
thi tt…then use thi B d E
- let rec filter f l = match l with
this pattern… this Body Expr
| [] -> []| (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];;# let list1 = [1;31;12;4;7;2;10];; # filter is5lt list1 ;;val it : int list = [31;12;7;10] # filter is5gte list1;;gval it : int list = [1;4;2] # filter even list1;;val it : int list = [12;4;2;10]
Put it together: a “partition” functionPut it together: a partition function
# let partition f l = (filter f l filter (neg f) l);# 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 ;# p ;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 A little trick …# 2 <= 3;; …val it : bool = trueval 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;
# let is5Lt = lt 5;val is5lt : int -> bool = fn;# is5lt 10; val it : bool = true;val it : bool = true;# is5lt 2;val it : bool = false;
Put it together: a “quicksort” functionPut it together: a quicksort function
let rec sort xs = match xs with|| [] -> []| (h::t) ->
let (l,r) = partition ((<) h) t in(sort l)@(h::(sort r))(sort l)@(h::(sort r))
;;
Now lets begin at the beginning Now, lets begin at the beginning …
News News
PA 1 due 5pm tomorrow•PA 1 due 5pm tomorrow
PA 2 t t•PA 2 out tomorrow
Begin at the beginning Begin at the beginning …Exec-time“D i ”
Expressions (Syntax) Values (Semantics)“Dynamic”
Types
Compile-time“Static”
1. Programmer enters expression2 ML checks if expression is “well-typed”
yp
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 expr3. ML evaluates expression to compute value
• Of the same “type” found in step 2
Base Type: intBase Type: int2; 2 i:int i ⇒ ii
2+3; 5e1:int e2:inte1+e2:int
e1⇒v1 e2⇒v2e1+e2⇒v1+v2
e1 + e2
7-4; 3 e1 - e2 e1:int e2:inte1-e2:int
e1⇒v1 e2⇒v2e1-e2⇒v1-v2
int :int 1⇒v1 2⇒v2(2+3)*(7-4); 15 e1 * e2 e1:int e2:int
e1 * e2:inte1⇒v1 e2⇒v2e1*e2⇒v1*v2
Expressions built from sub-expressionsTypes computed from types of sub-expressionsTypes computed from types of sub expressionsValues computed from values of sub-expressions
Base Type: floatBase Type: float2.0 2.0 r:float r ⇒ re
2.0 +.3.0 5.0e1:float e1:floate1+.e2:float
e1⇒v1 e2⇒v2e1+.e2⇒v1+.v2
e1 +. e2
7.0 –. 4.0 3.0 e1 -. e2 e1:float e2:floate1-.e2:float
e1⇒v1 e2⇒v2e1-.e2⇒v1-.v2
float :float 1⇒v1 2⇒v2(2.0 +. 3.0) /.(7.0 -. 4.0)
1.66.. e1 /. e2e1:float e2:floate1/.e2:float
e1⇒v1 e2⇒v2e1/.e2⇒v1/.v2
Expressions built from sub-expressionsTypes computed from types of sub-expressionsTypes computed from types of sub expressionsValues computed from values of sub-expressions
Base Type: stringBase Type: string“ab” “ab” s:string s ⇒ ss
“ab” ^ “cd” “abcd”e1:string e2:string
e1^e2:stringe1⇒v1 e2⇒v2e1^e2⇒v1^v2
e1^e2
Expressions built from sub-expressionsTypes computed from types of sub-expressionsTypes computed from types of sub expressionsValues computed from values of sub-expressions
Base Type: boolBase Type: booltrue true b:bool b ⇒ bb
2 < 3 truee1:T e2:Te1<e2 : bool
e1⇒v1 e2⇒v2e1<e2 ⇒ v1<v2
e1 < e2
not(2<3) false not e e : bool not e : bool
e ⇒ vnot e ⇒ not v
T :T 1⇒v1 2⇒v2(“ab”=“cd”) false e1 = e2
e1:T e2:Te1=e2 : bool
e1⇒v1 e2⇒v2e1=e2 ⇒ v1=v2
not (2<3) e :bool e :bool e1⇒v1 e2⇒v2not (2<3)&&
(“ab”=“cd”)
false e1 && e2e1:bool e2:boole1&&e2:bool
e1⇒v1 e2⇒v2e1&&e2⇒ v1 && v2
Base Type: boolBase Type: bool
• Equality testing built-in for all expr, values, typesEquality testing built in for all expr, values, types– but compared expressions must have same type
• … except for ?
T :T 1⇒v1 2⇒v2
p
(“ab”=“cd”) false e1 = e2e1:T e2:Te1=e2 : bool
e1⇒v1 e2⇒v2e1=e2 ⇒ v1=v2
Type ErrorsType Errors“pq” ^ 9; e1:string e2:string
i
(2 + “a”);
e1^e2:string
e1:int e2:int+ : inte1 + e2 : int
• Expressions built from sub-expressionsp p• Types computed from types of sub-expression• If a sub-expression is not well-typed then whole p yp
expression is not well-typed
0 * (2 + “a”);
Complex types: TuplesComplex types: Tuples
(2+2 , 7>8); (4,false)
i * b lint * bool
1 T1 2:T2 e1⇒v1 e2⇒v2e1:T1 e2:T2(e1,e2) : T1 * T2
e1⇒v1 e2⇒v2(e1,e2)⇒ (v1,v2)
Complex types: TuplesComplex types: Tuples• Can be of any fixed size
(9-3,“ab”^“cd”,7>8) (6, “abcd”, false)
(int * string * bool)(int * string * bool)
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 • Tuples can be nested in other tuples
Complex types: RecordsComplex types: Records
{name=“ranjit” ;{name ranjit ; age=31;pass=false}
{ name : string, age : int, pass : bool}
Records are tuples with named elements…
{name=“ranjit”;age=31;pass=false}.age int 31
int 31{age=31;name=“ranjit”;pass=false}.age
boolfalse{age=31;name=“ranjit”;pass=false}.pass
Complex types: ListsComplex types: Lists[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]]; (int list) list[[1];[2;3];[4;5;6]];
• Unbounded size• Can have lists of anything (e.g. lists of lists)Can have lists of anything (e.g. lists of lists)
Complex types: ListsComplex types: Lists[]:’a list [] ⇒ [][]
e1⇒v1 e2⇒v2 e3⇒v3[ 1 2 ]
[e1;e2;e3;…] e1:T e2: T e3: T … T li [e1;e2;…] ⇒ [v1;v2;…]
[e1;e2;e3;…][e1;e2;e3;…] : T list
All elements have the same type
[1; “pq”]
Complex types: list constructComplex types: list ..construct1::[2;3] [1;2;3]Cons “operator”
int list
p
e1⇒v1 e2 ⇒ v2 e1::e2 ⇒ v1::v2
e1:T e2: T list e1::e2 : T list
1::[“b”; “cd”];1::[ b ; cd ];
Can only “cons” element to a list of same type
Complex types: list constructComplex types: list …construct[1;2]@[3;4] [1;2;3;4]Append “operator” [ ]
int list
pp p
e1⇒v1 e2 ⇒ v2 e1@e2 ⇒ v1@v2
e1:T list e2: T list e1@e2 : T list
1@[“b”; “cd”];
C l d li t f th t
[1]@[“b”; “cd”];
Can only append lists of the same type
Complex types: list deconstructComplex types: list … deconstruct
Reading the elements of a list:g• 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]
int list
[“a”;“b”; “cd”] hd [“a”;“b”;“cd”] “a”
string
tl [“a”;“b”;“cd”] [“b”; “cd”]
string list
[(1,“a”);(7,“c”)] hd [(1,“a”);(7,“c”)] 1
int
[(7; “c”]
(int * string) list
tl [(1,“a”);(7,“c”)]
[[];[1;2;3];[4;5]] hd [[];[1;2;3];4;5] 1
int list
[2;3;4;5]
int list list
tl [[];[1;2;3];4;5]
List: Heads and TailsList: Heads and Tails
Head e ⇒v1::v2 e :T listHeadhd e ⇒ v1hd e : T
Tail e ⇒ v1::v2 tl e ⇒ v2
e :T listtl e : T list
(hd [[] [1 2 3]]) (hd [[] [“ ”]])(hd [[];[1;2;3]]) = (hd [[];[“a”]])
int list string liste1:T e2:Te1=e2 : bool
RecapRecapExec-time“D i ”
Expressions (Syntax) Values (Semantics)“Dynamic”
Types
Compile-time“Static”
1. Programmer enters expression2 ML checks if expression is “well-typed”
yp
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 expr3. ML evaluates expression to compute value
• Of the same “type” found in step 2
RecapRecap
• Integers: + - *Integers: • floats: +. -. *.• Booleans: = < && || not• Booleans: =,<, &&, ||, not• Strings: ^
• Tuples (Records)d b f l f d ff– Fixed number of values, of different types
• Lists: ::,@,hd,tl,null– Unbounded number of values, of same type
If-then-else expressionsIf then else expressionsif (1 < 2) then 5 else 10 5
i t
if (1 < 2) then [“ab”,“cd”] else [“x”] [“ab”,“cd”]
int
t i li t
If-then-else is also an expression! C i i th l b h
string list
Can use any expression in then, else branchif 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 expressionsIf then else expressionsif (1 < 2) then [1;2] else 5
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 e1 : bool e2: T e3: T if e1 then e2 else e3 : T
If-then-else expressionsIf then else expressions
e1 : bool e2: T e3: T e1 : 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 [] []
int list
if 1<2 then [] else [“a”] []
string list
(if 1>2 then [1,2] else [])=(if 1<2 then [] else [“a”])
Next: VariablesNext: Variables Variables and BindingsVariables and BindingsQ: 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 eto the variable x”
Variables and BindingsVariables and Bindings
# let x = 2+2;;# let x 2+2;;val x : int = 4# let y = x * x * x;;val y : int = 64val y : int = 64# let z = [x;y;x+y];;val z : int list = [4;64;68]
Later declared expressions can use x– Most recent “bound” value used for evaluation
Sounds like C/Java ? Sounds like C/Java ? NO!
Environments (“Phone Book”)Environments ( Phone Book )How ML deals with variables• Variables = “names”• Values = “phone number”Values phone number
x 4 : inty 64 : intz [4;64;68] : int list
M M
z [4;64;68] : int listx 8 : int
Environments and EvaluationEnvironments and EvaluationML begins in a “top-level” environment • Some names bound
let x = e;;let x = e;;
ML program = Sequence of variable bindingsML program Sequence of variable bindings
Program evaluated by evaluating bindings in orderProgram evaluated by evaluating bindings in order1. Evaluate expr e in current env to get value v : t2 Extend env to bind x to v : t2. Extend env to bind x to v : t(Repeat with next binding)
EnvironmentsEnvironments“Phone book”• Variables = “names”• Values = “phone number”Values phone number
1 Evaluate: 1. Evaluate: Find and use most recent value of variable
2. Extend:2. Extend:Add new binding at end of “phone book”
Example
# let x = 2+2;;
ExampleM M
# ;;val x : int = 4
# let y = x * x * x;;
x 4 : intM M
# let y = x x x;;val y : int = 64
# let z = [x;y;x+y];;
x 4 : inty 64 : int
M M
# let z = [x;y;x+y];;val z : int list = [4;64;68]
x 4 : inty 64 : int
M M
# let x = x + x ;;val x : int = 8
z [4;64;68] : int list
x 4 : intM M
y 64 : intz [4;64;68] : int listx 8 : intNew binding!
EnvironmentsEnvironments1. Evaluate: Use most recent bound value of var 2 Extend: Add new binding at end2. Extend: Add new binding at end
How is this different from C/Java’s “store” ?
# let x = 2+2;;val x : int = 4 x 4 : int
M M
# let f = fun y -> x + y;val f : int -> int = fn x 4 : int
f fn <code, >: int->int
M M
# let x = x + x ;val x : int = 8
New binding:
f fn code, : int int
# f 0;val it : int = 4
• No change or mutation• Old binding frozen in f
EnvironmentsEnvironments1. Evaluate: Use most recent bound value of var 2 Extend: Add new binding at end2. Extend: Add new binding at end
How is this different from C/Java’s “store” ?
# let x = 2+2;val : int x = 4 x 4 : int
M M
# let f = fun y -> x + y;val f : int -> int = fn x 4 : int
f fn <code, >: int->int
M M
# let x = x + x ;val x : int = 8; x 4 : int
f fn <code >: int >int
M M
f fn code, : int int
# f 0;val it : int = 4
f fn <code, >: int->int
x 8 : int
EnvironmentsEnvironments1. Evaluate: Use most recent bound value of var 2 Extend: Add new binding at end2. Extend: Add new binding at end
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
Binding used to eval (f )# let x = x + x ;val x : int = 8 x 4 : int
f fn <code, >: int->int
M M
Binding used to eval (f …)
# f 0;val it : int = 4
f fn code, : int int
x 8 : int
Binding for subsequent x
Cannot change the worldCannot change the worldCannot “assign” to variables• Can extend 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 x = 2+2;; Binding used to eval (f …);;val x : int = 4# let f = fun y -> x + y;;val f : int -> int = fn# let x = x + x ;;
x 4 : intf fn <code, >: int->int
M M
g ( )
# let x = x + x ;;val x : int = 8;# f 0;;val it : int = 4
,
x 8 : int
Binding for subsequent x
Cannot change the worldCannot change the world
Q: Why is this a good thing ?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 debuggingLocalizes debugging– Localizes reasoning about the program
N “ h i g” il li i g– No “sharing” means no evil aliasing
Examples of no sharingExamples of no sharingRemember: 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 youg p y
• Compiler’s job is to optimize code • Efficiently implement these “no-sharing” semantics
Y j b i t th i lifi d ti • Your job is to use the simplified semantics • Write correct, cleaner, readable, extendable systems
Function bindingsFunction bindingsFunctions are values, can bind using val
let fname = fun x -> e ;;
Problem: Can’t define recursive functions !• fname is bound after computing rhs value
( “ ld”) bi di f f i id • 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)
Local bindingsLocal bindings
So far: “global” bindings So far: “global” bindings (Remain till a re-binding)(Remain till a re binding)
Next: “local” variablesNext: local variables•Useful inside functions•Avoid repeating computations
Make functions more readable• Make functions more readable
Local bindingsLocal bindings
let x = e1 inlet x = e1 in e2
;;
Let-in is an expression!Let in is an expression!
Evaluating let-in in env E:g1. Evaluate expr e1 in env E to get value v : t2 Use extended E [x a v : t] to evaluate e22. Use extended E [x a v : t] to evaluate e2
Local bindingsLocal bindingsEvaluating let-in in env E:1. Evaluate expr e1 in env E to get value v : t2. Use extended E [x a v : t] to evaluate e2
let x = 10
M M
x = 10in
*x 10 : intM M
x * x;;
M MM M
Let-in is an expression!Let in is an expression!Evaluating let-in in env E:1. Evaluate expr e1 in env E to get value v : t2. Use extended E [x a v : t] to evaluate e2
let y =l t
M M
let x = 10
M Minx * x
x 10 : intM M
;;M M
y 100 : int
Nested bindingsNested bindingsEvaluating let-in in env E:1. Evaluate expr e1 in env E to get value v : t2. Use extended E [x a v : t] to evaluate e2 let
x = 10in
M M
M Min(let
y = 20i
x 10 : int
inx * y)
+ xx 10 : intM M
y 20 : int
;;x 10 : intM M
M M
Nested bindingsNested bindings
letlet x = 10
ininlet
20
let x = 10 in let y = 20 iny = 20
in*
let y 20 inx * y
;;x * y;;
;;
Correct Formattingg
ExampleExample
let rec filter (f,l) = if l = [] then
[]else
let h = hd l inlet t = filter (f, tl l) inif (f h) then h::t else t
Nested function bindings Nested function bindings
l t 20let a = 20;;
let f x =let f x let y = 10 in let g z = y + z in
Env frozen with function• Used to evaluate fun application
V l i li ti th g y
a + (g x);;
• Values in application are those frozen in env at definition
f 0;
RecapRecap
Variables are names for valuesVariables are names for values– Environment: dictionary/phonebook
Most recent binding used– Most recent binding used– Entries never changed, new entries added
Environment frozen at fun definition– Re-binding vars cannot change function behavior– Same I/O behavior at every call
RecapRecap
• Build complex expressions with local bindingsBuild complex expressions with local bindings– let-in expression– The let-binding is visible (in scope) inside in-expression– Elsewhere the binding is not visible
Static/Lexical ScopingStatic/Lexical Scoping
• For each occurrence of a variable, there is a For each occurrence of a variable, there is a unique place in program text where the variable was 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 assignedDon t have to figure out where a variable got assigned– Unique, statically known definition for each occurrence
Next: FunctionsNext: Functions
Expressions Values
Typesyp
Q: What’s the value of a function ?