lazy evaluation
DESCRIPTION
TRANSCRIPT
lazy evaluationhey, i’m fronx.
berlin compiler meetup, feb 2014
bottom
infinite lists
approximation
eager
strictness in an argument
graph reduction
leftmost outermost
normal order
sharing
full laziness
WHNF
thunk
call by need
redex
constant subexpression
instantiation
not
- user-code performance optimization
- how and why to avoid capture
- supercombinators / λ-lifting
- representation in memory
- garbage collection
- compiler optimizations
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
semantics
evaluation strategy
strict non-strict
eager lazy
vs.
as a language property
expressions can
have a value
even if some of their subexpressions
do nothttp://www.haskell.org/haskellwiki/Non-strict_semantics
non-strict
expressions can
have a value
even if some of their subexpressions
do nothttp://www.haskell.org/haskellwiki/Non-strict_semantics
strictnot
expressions can
have a value
even if some of their subexpressions
do nothttp://www.haskell.org/haskellwiki/Non-strict_semantics
strictnot
… have a value
“not have a value”?
loop :: Int -> Int loop n = 1 + loop n
loop :: Int -> Int loop n = 1 + loop n
repeat :: Int -> [Int] repeat n = n : repeat n !
> repeat 1 [1,1,1,1,1,1,1,1,1,1,1,1,…
how is this useful?
infinite lists are awesome.
nat :: [Int] nat = nat' 1 where nat' n = n : nat' (n + 1) !
take 5 (map sqr nat) == [1,4,9,16,25]
http://mybroadband.co.za/news/wp-content/uploads/2013/03/Barack-Obama-Michelle-Obama-not-bad.jpg
⊥”bottom”
“a computation which never completes successfully”
http://www.haskell.org/haskellwiki/Bottom
bottom :: a bottom = bottom
⊥
undefined (throws error)
take 0 _ = []
take n (x : xs) = x : take (n - 1) xs
take n [] = []
!
take 2 (1:1:1:undefined) == take 2 (1:1:undefined) == [1,1]
repeat n = n : repeat n !
repeat 1 => 1 : ⊥ => 1 : 1 : ⊥ => 1 : 1 : 1 : ⊥ => 1 : 1 : 1 : 1 : ⊥ …
repeat n = n : repeat n !
repeat 1 => 1 : ⊥ => 1 : 1 : ⊥ => 1 : 1 : 1 : ⊥ => 1 : 1 : 1 : 1 : ⊥ …
Terms
approximation,
more defined
http://en.wikibooks.org/wiki/Haskell/Denotational_semantics
strict / non-strict functions
a function is strict iff
f ⊥ = ⊥
strictness of functions
id = λ x . x
( λ x . x ) ⊥ = ⊥
strict
strictness of functions
one = λ x . 1
( λ x . 1 ) ⊥ = 1
non-strict
strictness of functions
second = λ x y . y
( λ x y . y ) ⊥ 2 = 2
non-strict
strict in its 2nd argument
in its 1st argument
repeat n = n : repeat n !
repeat 1 => 1 : ⊥ => 1 : 1 : ⊥ => 1 : 1 : 1 : ⊥ => 1 : 1 : 1 : 1 : ⊥ …
Question: Is repeat strict or non-strict
(“lazy”)?
repeat n = n : repeat n !
repeat ⊥ = ⊥ : repeat ⊥
repeat n = n : repeat n !
repeat ⊥ = ⊥ : repeat ⊥
strict
one x = 1 one ⊥ = 1
non-strict
evaluation strategies
eager lazyfirst 1 (loop 2)
=>
first 1 (loop 2)
=>
first x y = x
eager lazyfirst 1 (loop 2)
=> first 1 (2 + (loop 2))
=> first 1 (2 + (2 + (loop 2)))
=
first 1 (loop 2)
= 1
first x y = x
⊥
eager lazyfirst 1 (loop 2)
=> first 1 (2 + (loop 2))
=> first 1 (2 + (2 + (loop 2)))
=
first 1 (loop 2)
= 1
first x y = x
⊥redex: reducible expression
eager lazyfirst (loop 1) 2
=> first (1 + (loop 1)) 2
=> first (1 + (1 + (loop 1))) 2
=
first (loop 1) 2
=> loop 1
=> 1 + (loop 1)
=> 1 + (1 + (loop 1))
=
first x y = x
⊥⊥
eager lazynat 1
=>
nat 1
=>
nat n = n : nat (n + 1)
eager lazynat 1
=> 1 : nat (1+1)
=> 1 : nat 2
=> 1 : 2 : nat (2 + 1)
=
nat 1
= 1 : nat (1 + 1)
nat n = n : nat (n + 1)
⊥
the function is still strict! lazy evaluation allows
you to look at intermediate results, though.
eager lazynat 1
=> 1 : nat (1+1)
=> 1 : nat 2
=> 1 : 2 : nat (2 + 1)
=
nat 1
= 1 : nat (1 + 1)
nat n = n : nat (n + 1)
⊥
Question: How do you define the
point where it has to stop?
weak head normal form
expression head is one of:
- variable - data object - built-in function - lambda abstraction
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
weak head normal form
An expression has no top-level redex
iff it is in
weak head normal form.
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
weak head normal form
examples:
3 1 : (2 : …) == CONS 1 (CONS 2 …) + (- 4 3) (λx. + 5 1)
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
weak head normal form
Ignore inner redexes as long as you can.
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
result:
graph reductiona way to model / implement
lazy evaluation
first = λ x y . x first = λ x . λ y . x first 1 ⊥ @
λx
λy
x
@
1⊥
graph reduction
first = λ x y . x first = λ x . λ y . x first 1 ⊥ @
λx
λy
x
@
1⊥
graph reduction
leftmost outermost reduction
= normal-order
reduction
first = λ x y . x first = λ x . λ y . x first 1 ⊥ @
λx
λy
x
@
1⊥
@
λy
1
⊥
1
graph reduction
instantiate the lambda
body
sqr = λ x . x * x sqr 2 @
λx
@
@
2
graph reduction
* x
x
@
@
* 2
2
4
sqr (6 + 1)
@
λx
@
@
@
graph reduction
* x
x
@
+ 6
1
sqr (6 + 1)
@
λx
@
@
@
graph reduction
* x
x
@
@
*
@
+ 6
1
@
@
+ 6
1
@
@
+ 6
1
…
call by name
sqr (6 + 1)
@
λx
@
@
@
graph reduction
* x
x
@
+ 6
1
@
@@
*@
+ 6
1 @
@ 7*
49
sharing
call by need
+ call by name + sharing + —————— + call by need
( λ x . λ x . x ) 1 2
@
λx
λx
@
shadowing
x
1
2 @
λx
x
2
2
full lazinessλy. + y (sqrt 4)
@λy
@@
λx
@
“x(1), x(2)”
+ y sqrt 4
constant subexpression
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
maximal free expression
a free expression which is not a subexpression
of another free expression
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
full lazinessλy. + y (sqrt 4)
@λy
@@
λx
@
“x(1), x(2)”
+ y sqrt 4
maximal free expression in $
$
free subexpression
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
full lazinessλy. + y (sqrt 4)
@λy
@
@λx
@
“x(1), x(2)”
+ y
sqrt 4
Extract all the maximal free expressions! !
|o/sqrt4
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
full laziness
A Unified Approach to Fully Lazy Sharing, Balabonski 2012
fully lazy λ-lifting
extract MFEs via closures
sharing via labelling
variants
(sorry, no time for details.)
SPJ87
Sestoft97
Blanc/Levy/Maranget07
so how does take work?
take 0 _ = []
take n (x : xs) = x : take (n - 1) xs
take n [] = []
so how does take work?
take 0 _ = []
take n (x : xs) = x : take (n - 1) xs
take n [] = []
patterns
- variable - constant - sum constructor - product constructor
product-matching
= zeroPair (x,y) = 0
!
= Eval [[ zeroPair ]] ⊥ = ⊥
= Eval [[ zeroPair ]] ⊥ = 0 lazy product-matching
strict product-matching
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
lazy product-matching
= zeroPair (x,y) = 0
!
= Eval [[ zeroPair ]] ⊥
= Eval [[ λ(Pair x y). 0 ]] ⊥
= Eval [[ λx. λy. 0 ]] (SEL-PAIR-1 ⊥) (SEL-PAIR-2 ⊥)
= Eval [[ λy. 0 ]] (SEL-PAIR-2 ⊥)
= 0
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
lazy product-matching
SEL-PAIR-1 (Pair x y) = x
SEL-PAIR-1 ⊥ = ⊥
SEL-PAIR-2 (Pair x y) = y
SEL-PAIR-2 ⊥ = ⊥
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
lazy product-matching
SEL-PAIR-1 (Pair x y) = x
SEL-PAIR-1 ⊥ = ⊥
SEL-PAIR-2 (Pair x y) = y
SEL-PAIR-2 ⊥ = ⊥
SEL-constr-i (constr a1 … ai … an) = ai
SEL-constr-i ⊥ = ⊥
http://research.microsoft.com/en-us/um/people/simonpj/papers/slpj-book-1987/
summary
- f is strict ⇔ f ⊥ = ⊥
- graph reduction
- sharing
- lazy pattern-matching
- go and read those papers/books
okay. that’s it for today.
http://www.techworld.com.au/article/261007/a-z_programming_languages_haskell/?pp=7
“Laziness kept us pure.”
Simon Peyton Jones
https://twitter.com/evilhaskelltips/status/429788191613140992
“If your program is slow, don't bother profiling. Just scatter bang patterns throughout your code like confetti.
They're magic!”