Professional Documents
Culture Documents
CS Advising Fair
2:30-4:00 today after class
Maxwell Dworkin lobby
2
CS Advising Fair
2:30-4:00 today after class
Maxwell Dworkin lobby
2
Big ideas
Streams
Postponing computation
Lazy recomputation
Laziness through the ages
An application: Taylor series and !
Streams
type 'a stream =
Cons of 'a * 'a stream ;;
Streams
type 'a stream =
Cons of 'a * 'a stream ;;
let head (s: 'a stream) : 'a =
match s with
| Cons (h, _) -> h ;;
Streams
type 'a stream =
Cons of 'a * 'a stream ;;
let head (Cons(h,_): 'a stream) : 'a =
h ;;
let tail (Cons(_,t): 'a stream) : 'a stream =
t ;;
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
#
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
10
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Error: Unbound value ones
#
11
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Error: Unbound value ones
# let rec ones = Cons(1, ones) ;;
12
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Error: Unbound value ones
# let rec ones = Cons(1, ones) ;;
val ones : int stream = Cons (1, <cycle>)
#
13
Streams
# type 'a stream =
Cons of 'a * 'a stream ;;
type 'a stream = Cons of 'a * 'a stream
# let ones = Cons(1, ones) ;;
Error: Unbound value ones
# let rec ones = Cons(1, ones) ;;
val ones : int stream = Cons (1, <cycle>)
# head ones;;
- : int = 1
14
Streams
14
15
Cons( , )
15
ones
15
Cons( , )
ones
16
Cons(1, )
ones
17
Cons(1, )
ones
18
Cons(1, )
19
19
x
19
x
20
x
21
22
23
23
24
Postponing computation
# type 'a stream =
Cons of 'a * 'a stream ;;
# let rec smap (f : 'a -> 'b) (s : 'a stream)
: ('b stream) =
Cons(f (head s), smap f (tail s)) ;;
25
Postponing computation
# type 'a stream =
Cons of 'a * 'a stream ;;
# let rec smap (f : 'a -> 'b) (s : 'a stream)
: ('b stream) =
Cons(f (head s), smap f (tail s)) ;;
Lazy streams
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
26
Lazy streams
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
# let rec ones : int stream =
fun () -> Cons(1, ones) ;;
27
Lazy streams
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
# let rec ones () : int str =
Cons(1, ones) ;;
# let head (s : 'a stream) : 'a =
match s() with
| Cons(h, t) -> h ;;
# let tail (s : 'a stream) : 'a stream =
match s() with
28
| Cons(h, t) -> t ;;
Lazy streams
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
# let rec ones () : int str =
Cons(1, ones) ;;
# let head (s : 'a stream) : 'a =
match s() with
| Cons(h, t) -> h ;;
# let tail (s : 'a stream) : 'a stream =
match s() with
28
| Cons(h, t) -> t ;;
Lazy
streams
Cons(1,
ones) ;;
Lazy
streams
Cons(1,
ones) ;;
Returning
a function
# let tail (s : 'a stream)
: 'a stream
=
match s() with
| Cons(h, t) -> t
delays
evaluating the recursive call
to smap.
;;
| Cons(h, t) -> t ;;
Lazy streams
30
| Cons(h, t) -> t ;;
Lazy streams
fun () ->
An
infinite
map...
Cons(f (head s), smap
f (tail s)) ;
30
| Cons(h, t) -> t ;;
Lazy streams
fun () ->
An
infinite
map...
Cons(f (head s), smap
f (tail s)) ;
30
31
32
32
7 ...
7 ...
1 ...
Ones
32
7 ...
1 ...
8 ...
Ones
Add them
32
7 ...
1 ...
8 ...
7 ...
8 ...
Ones
Add them
Prepend a zero
0
32
33
33
34
35
35
36
36
...
...
Fibonaccis
36
...
...
Fibonaccis
...
Tail of Fibonaccis
36
...
...
Fibonaccis
...
...
Tail of Fibonaccis
Sum
36
...
...
...
...
Fibonaccis
Tail of Fibonaccis
Sum
37
...
...
...
...
Fibonaccis
Tail of Fibonaccis
Sum
38
...
...
...
...
Fibonaccis
Tail of Fibonaccis
Sum
39
...
Lazy recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
40
Lazy recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
40
Lazy recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
# let rec fibs =
fun () -> Cons(0,
fun () -> Cons(1,
(smap2 (+) fibs (tail fibs)))) ;;
41
Lazy recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = unit -> 'a str ;;
# let rec fibs =
fun () -> Cons(0,
fun () -> Cons(1,
(smap2 (+) fibs (tail fibs)))) ;;
42
t := Evaluated (f());
force t ;;
42
42
42
Avoiding recomputation
# let rec fib x =
if x < 2 then x
else (fib (x-1)) + (fib (x-2)) ;;
# let fib42 : int thunk ref =
ref (Unevaluated (fun () -> fib 42)) ;;
# force fib42 ;;
- : int = 267914296
# force fib42 ;;
- : int = 267914296
43
Avoiding recomputation
# let rec fib x =
if x < 2 then x
Avoiding recomputation
# let rec fib x =
if x < 2 then x
12.7 seconds
# force fib42 ;;
- : int = 267914296
43
Avoiding recomputation
# let rec fib x =
if x < 2 then x
12.7 seconds
# force fib42 ;;
- : int = 267914296
43
0 microseconds
find this code in: streams.ml
44
44
A delayed memoized
computation of an 'a:
-> 'a
'a Lazy.t
44
A delayed memoized
computation of an 'a:
-> 'a
'a Lazy.t
| Unevaluated f ->
t := Evaluated (f());
force t ;;
44
A delayed memoized
computation of an 'a:
-> 'a
'a Lazy.t
| Unevaluated f ->
t := Evaluated (f());
force t ;;
44
lazy expr
ref (Unevaluated (fun () -> expr))
12.7 seconds
# Lazy.force fib42 ;;
- : int = 267914296
45
12.7 seconds
# Lazy.force fib42 ;;
- : int = 267914296
45
0 microseconds
find this code in: streams.ml
Avoiding recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = 'a str Lazy.t ;;
# let head (s : 'a stream) : 'a =
match Lazy.force s with
| Cons(h, _) -> h ;;
# ...
46
Avoiding recomputation
# type 'a str = Cons of 'a * 'a stream
and 'a stream = 'a str Lazy.t ;;
# ...
# let rec fibs =
lazy (Cons(0,
lazy (Cons(1,
smap2 (+) fibs (tail fibs))))) ;;
# first 10 fibs ;;
- : int list = [0; 1; 1; 2; 3; 5; 8; 13; 21; 34]
47
A history of laziness
1965: Landin describes streams saying There
is a relationship between lists and functions.
In this relationship a nonnull list L is mirrored
by a none-adic function S that produces a 2list consisting of (1) the head of L, and (2) the
function mirroring the tail of L.
Peter J. Landin
48
A history of laziness
1965: Landin describes streams
1971: Wadsworth proposes the lazy lambda
calculus
1976: Friedman and Wise publish Cons
should not evaluate its arguments
1980: Burstall proposes Hope, a lazy
functional programming language (with
algebraic data types)
Rod Burstall
49
A history of laziness
1965: Landin describes streams
1971: Wadsworth proposes the lazy lambda
calculus
1976: Friedman and Wise publish Cons
should not evaluate its arguments
1980: Burstall proposes Hope, a lazy
functional programming language (with
algebraic data types)
1985: The Miranda lazy functional language is
released
1990: First version of Haskell leads to
convergence of lazy languages
50
Haskell Curry
Application: approximating !
arctan x = x
4
=1
=4
51
x3 x5
+
3
5
1 1
+
3 5
4 4
+
3 5
x7
+
7
1
+
7
4
+
7
Application: approximating !
!
arctan x = x
4
=1
=4
51
arctan 1
x3 x5
+
3
5
1 1
+
3 5
4 4
+
3 5
x7
+
7
1
+
7
4
+
7
Application: approximating !
!
arctan x = x
4
=1
=4
51
arctan 1
x3 x5
+
3
5
1 1
+
3 5
4 4
+
3 5
x7
+
7
1
+
7
4
+
7
Application: approximating !
!
arctan x = x
4
=1
=4
51
arctan 1
x3 x5
+
3
5
1 1
+
3 5
4 4
+
3 5
x7
+
7
1
+
7
4
+
7
Application:
=4
4/3 + 4/5
4/7 +
52
Application:
=4
4/3 + 4/5
4/7 +
# first 5 odds ;;
- : int list = [1; 3; 5; 7; 9]
# first 5 alt_signs ;;
- : int list = [1; -1; 1; -1; 1]
# first 5 pi_stream ;;
- : float list =
[4.; -1.33333333333333326; 0.8; -0.571428571428571397;
0.44444444444444442]
53
Application:
=4
4/3 + 4/5
4/7 +
# let pi_approx n =
List.fold_left ( +. ) 0.0 (first n pi_stream) ;;
# pi_approx 10 ;;
- : float = 3.04183961892940324
# pi_approx 100 ;;
- : float = 3.13159290355855369
# pi_approx 1000 ;;
- : float = 3.14059265383979413
# pi_approx 10000 ;;
- : float = 3.14149265359003449
# pi_approx 100000 ;;
- : float = 3.14158265358971978
54
Partial sums
55
a series
partial sums
6 10 15 21 28 ...
6 10 15 21 ...
+ series
= partial sums
6 10 15 21 28 ...
7 ...
7 ...
Partial sums
56
a series
partial sums
6 10 15 21 28 ...
6 10 15 21 ...
+ series
= partial sums
6 10 15 21 28 ...
7 ...
7 ...
Partial sums
57
a series
partial sums
6 10 15 21 28 ...
6 10 15 21 ...
+ series
= partial sums
6 10 15 21 28 ...
7 ...
7 ...
Partial sums
58
a series
partial sums
6 10 15 21 28 ...
6 10 15 21 ...
+ series
= partial sums
6 10 15 21 28 ...
7 ...
7 ...
Partial sums
a series
partial sums
6 10 15 21 28 ...
6 10 15 21 ...
+ series
= partial sums
6 10 15 21 28 ...
7 ...
7 ...
Application:
=4
4/3 + 4/5
4/7 +
59
Application:
=4
4/3 + 4/5
4/7 +
60
Application:
=4
4/3 + 4/5
4/7 +
61
Laziness elsewhere
OCaml is an eager language by default but with minimal support
for lazy constructs
Other functional languages (especially Haskell) are lazy by default
Much easier to program with infinite structures
Harder to reason about space and time
Complex interactions with side effects
Echoes in many areas, especially concurrency:
Unix pipes
TCP sockets
async libraries
62
63
64
64
let rec f =
fun n -> if n = 0 then 1
else n * f (n-1) in
f 3
65
let rec f =
fun n -> if n = 0 then 1
else n * f (n-1) in
f 3
65
let rec f =
fun n -> if n = 0 then 1
else n * f (n-1) in
f 3
if 3 = 0 then 1
else 3 * f (n-1)
65
let rec f =
fun n -> if n = 0 then 1
else n * f (n-1) in
f 3
if 3 = 0 then 1
else 3 * f (n-1)
Free variable?!
65
66
67
let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f 2
if 2 = 0 then 1
else 2 * (let rec f =
(fun n -> if n = 0 then 1
68
else n * f (n-1))
in f 2
if 2 = 0 then 1
else 2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
(2-1)
if false then 1
69
if 2 = 0 then 1
else 2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
(2-1)
if false then 1
else 2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
(2-1)
2 * (let rec f =
70
in f)
(2-1)
if false then 1
else 2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
(2-1)
2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
(2-1)
2 * (let rec f =
(fun n -> if n = 0 then 1
71
else n * f (n-1))
in f)
(2-1)
2 * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f)
1
2 * (if 1 = 0 then 1
72
2 * (1 * (if 0 = 0 then 1
else n * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f) (0-1)))
2 * (1 * 1)
2 * 1
2
73
2 * (1 * (if 0 = 0 then 1
else n * (let rec f =
(fun n -> if n = 0 then 1
else n * f (n-1))
in f) (0-1)))
else n * f (n-1))
in f) (0-1)))
2 * (1 * 1)
2 * 1
2
73