0% found this document useful (0 votes)
10 views

Lec 3 Slides

Lecture notes of

Uploaded by

Palas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Lec 3 Slides

Lecture notes of

Uploaded by

Palas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

CSE341: Programming Languages

Lecture 3
Local Bindings;
Options;
Benefits of No Mutation

Dan Grossman
Spring 2019
Review
Huge progress already on the core pieces of ML:
• Types: int bool unit t1*…*tn t list t1*…*tn->t
– Types “nest” (each t above can be itself a compound type)
• Variables, environments, and basic expressions
• Functions
– Build: fun x0 (x1:t1, …, xn:tn) = e
– Use: e0 (e1, …, en)
• Tuples
– Build: (e1, …, en)
– Use: #1 e, #2 e, …
• Lists
– Build: [] e1::e2
– Use: null e hd e tl e

Spring 2019 CSE341: Programming Languages 2


Today
• The big thing we need: local bindings
– For style and convenience
– A big but natural idea: nested function bindings
– For efficiency (not “just a little faster”)

• One last feature for Problem 11 of Homework 1: options

• Why not having mutation (assignment statements) is a valuable


language feature
– No need for you to keep track of sharing/aliasing,
which Java programmers must obsess about

Spring 2019 CSE341: Programming Languages 3


Let-expressions

3 questions:

• Syntax: let b1 b2 … bn in e end


– Each bi is any binding and e is any expression

• Type-checking: Type-check each bi and e in a static


environment that includes the previous bindings.
Type of whole let-expression is the type of e.

• Evaluation: Evaluate each bi and e in a dynamic environment


that includes the previous bindings.
Result of whole let-expression is result of evaluating e.

Spring 2019 CSE341: Programming Languages 4


It is an expression

A let-expression is just an expression, so we can use it


anywhere an expression can go

Spring 2019 CSE341: Programming Languages 5


Silly examples
fun silly1 (z : int) =
let val x = if z > 0 then z else 34
val y = x+z+9
in
if x > y then x*2 else y*y
end
fun silly2 () =
let val x = 1
in
(let val x = 2 in x+1 end) +
(let val y = x+2 in y+1 end)
end

silly2 is poor style but shows let-expressions are expressions


– Can also use them in function-call arguments, if branches, etc.
– Also notice shadowing
Spring 2019 CSE341: Programming Languages 6
What’s new

• What’s new is scope: where a binding is in the environment


– In later bindings and body of the let-expression
• (Unless a later or nested binding shadows it)
– Only in later bindings and body of the let-expression

• Nothing else is new:


– Can put any binding we want, even function bindings
– Type-check and evaluate just like at “top-level”

Spring 2019 CSE341: Programming Languages 7


Any binding

According to our rules for let-expressions, we can define functions


inside any let-expression

let b1 b2 … bn in e end

This is a natural idea, and often good style

Spring 2019 CSE341: Programming Languages 8


(Inferior) Example

fun countup_from1 (x : int) =


let fun count (from : int, to : int) =
if from = to
then to :: []
else from :: count(from+1,to)
in
count (1,x)
end

• This shows how to use a local function binding, but:


– Better version on next slide
– count might be useful elsewhere

Spring 2019 CSE341: Programming Languages 9


Better:
fun countup_from1_better (x : int) =
let fun count (from : int) =
if from = x
then x :: []
else from :: count(from+1)
in
count 1
end
• Functions can use bindings in the environment where they are
defined:
– Bindings from “outer” environments
• Such as parameters to the outer function
– Earlier bindings in the let-expression

• Unnecessary parameters are usually bad style


– Like to in previous example
Spring 2019 CSE341: Programming Languages 10
Nested functions: style

• Good style to define helper functions inside the functions they


help if they are:
– Unlikely to be useful elsewhere
– Likely to be misused if available elsewhere
– Likely to be changed or removed later

• A fundamental trade-off in code design: reusing code saves


effort and avoids bugs, but makes the reused code harder to
change later

Spring 2019 CSE341: Programming Languages 11


Avoid repeated recursion
Consider this code and the recursive calls it makes
– Don’t worry about calls to null, hd, and tl because they
do a small constant amount of work
fun bad_max (xs : int list) =
if null xs
then 0 (* horrible style; fix later *)
else if null (tl xs)
then hd xs
else if hd xs > bad_max (tl xs)
then hd xs
else bad_max (tl xs)

let x = bad_max [50,49,…,1]


let y = bad_max [1,2,…,50]

Spring 2019 CSE341: Programming Languages 12


if hd xs > bad_max (tl xs)
Fast vs. unusable then hd xs
else bad_max (tl xs)

bm [50,…] bm [49,…] bm [48,…] bm [1]

bm [1,…] bm [2,…] bm [3,…] bm [50]

bm [3,…]
250


bm [2,…] bm [3,…] times

bm [3,…]

bm [50]

Spring 2019 CSE341: Programming Languages 13


Math never lies

Suppose one bad_max call’s if-then-else logic and calls to hd,


null, tl take 10-7 seconds
– Then bad_max [50,49,…,1] takes 50 x 10-7 seconds
– And bad_max [1,2,…,50] takes 1.12 x 108 seconds
• (over 3.5 years)
• bad_max [1,2,…,55]takes over 1 century
• Buying a faster computer won’t help much 

The key is not to do repeated work that might do repeated work


that might do…
– Saving recursive results in local bindings is essential…

Spring 2019 CSE341: Programming Languages 14


Efficient max

fun good_max (xs : int list) =


if null xs
then 0 (* horrible style; fix later *)
else if null (tl xs)
then hd xs
else
let val tl_ans = good_max(tl xs)
in
if hd xs > tl_ans
then hd xs
else tl_ans
end

Spring 2019 CSE341: Programming Languages 15


Fast vs. fast
let val tl_ans = good_max(tl xs)
in
if hd xs > tl_ans
then hd xs
else tl_ans
end

gm [50,…] gm [49,…] gm [48,…] gm [1]

gm [1,…] gm [2,…] gm [3,…] gm [50]

Spring 2019 CSE341: Programming Languages 16


Options

• t option is a type for any type t


– (much like t list, but a different type, not a list)

Building:
• NONE has type 'a option (much like [] has type 'a list)
• SOME e has type t option if e has type t (much like e::[])

Accessing:
• isSome has type 'a option -> bool
• valOf has type 'a option -> 'a (exception if given NONE)

Spring 2019 CSE341: Programming Languages 17


Example
fun better_max (xs : int list) =
if null xs
then NONE
else
let val tl_ans = better_max(tl xs)
in
if isSome tl_ans
andalso valOf tl_ans > hd xs
then tl_ans
else SOME (hd xs)
end

val better_max = fn : int list -> int option

• Nothing wrong with this, but as a matter of style might prefer not
to do so much useless “valOf” in the recursion

Spring 2019 CSE341: Programming Languages 18


Example variation
fun better_max2 (xs : int list) =
if null xs
then NONE
else let (* ok to assume xs nonempty b/c local *)
fun max_nonempty (xs : int list) =
if null (tl xs)
then hd xs
else
let val tl_ans = max_nonempty(tl xs)
in
if hd xs > tl_ans
then hd xs
else tl_ans
end
in
SOME (max_nonempty xs)
end
Spring 2019 CSE341: Programming Languages 19
Cannot tell if you copy
fun sort_pair (pr : int * int) =
if #1 pr < #2 pr
then pr
else (#2 pr, #1 pr)

fun sort_pair (pr : int * int) =


if #1 pr < #2 pr
then (#1 pr, #2 pr)
else (#2 pr, #1 pr)

In ML, these two implementations of sort_pair are indistinguishable


– But only because tuples are immutable
– The first is better style: simpler and avoids making a new pair in
the then-branch
– In languages with mutable compound data, these are different!

Spring 2019 CSE341: Programming Languages 20


Suppose we had mutation…
val x = (3,4)
val y = sort_pair x 3 4
x
somehow mutate #1 x to hold 5 ?
y
val z = #1 y ? 3 4

• What is z?
– Would depend on how we implemented sort_pair
• Would have to decide carefully and document sort_pair
– But without mutation, we can implement “either way”
• No code can ever distinguish aliasing vs. identical copies
• No need to think about aliasing: focus on other things
• Can use aliasing, which saves space, without danger
Spring 2019 CSE341: Programming Languages 21
An even better example
fun append (xs : int list, ys : int list) =
if null xs
then ys
else hd (xs) :: append (tl(xs), ys)
val x = [2,4]
val y = [5,3,0]
val z = append(x,y)

x 2 4
y 5 3 0
(can’t tell,
z 2 4 but it’s the
or
first one)
x 2 4
y 5 3 0
z 2 4 5 3 0
Spring 2019 CSE341: Programming Languages 22
ML vs. Imperative Languages

• In ML, we create aliases all the time without thinking about it


because it is impossible to tell where there is aliasing
– Example: tl is constant time; does not copy rest of the list
– So don’t worry and focus on your algorithm

• In languages with mutable data (e.g., Java), programmers are


obsessed with aliasing and object identity
– They have to be (!) so that subsequent assignments affect
the right parts of the program
– Often crucial to make copies in just the right places
• Consider a Java example…

Spring 2019 CSE341: Programming Languages 23


Java security nightmare (bad code)
class ProtectedResource {
private Resource theResource = ...;
private String[] allowedUsers = ...;
public String[] getAllowedUsers() {
return allowedUsers;
}
public String currentUser() { ... }
public void useTheResource() {
for(int i=0; i < allowedUsers.length; i++) {
if(currentUser().equals(allowedUsers[i])) {
... // access allowed: use it
return;
}
}
throw new IllegalAccessException();
}
}
Spring 2019 CSE341: Programming Languages 24
Have to make copies
The problem:

p.getAllowedUsers()[0] = p.currentUser();
p.useTheResource();

The fix:

public String[] getAllowedUsers() {


… return a copy of allowedUsers …
}

Reference (alias) vs. copy doesn’t matter if code is immutable!

Spring 2019 CSE341: Programming Languages 25

You might also like