In Haskell like languages evaluation is reducing expressions to their simplest form.
(5 + 4 - 2) * (5 + 4 - 2) ⇒ 49
Two options when expression includes function application.
square (5 + 4 - 2) ⇒ 49
Reduce arguments first (Innermost reduction / Eager Evaluation)
square (5 + 4 - 2) ⇒ square(7) ⇒ 7 * 7 ⇒ 49
Apply function first (Outermost reduction / Lazy Evaluation)
square (5 + 4 - 2) ⇒ (5 + 4 - 2) * (5 + 4 - 2) ⇒ 7 * 7 ⇒ 49
49
is the normal form of square (5 + 4 - 2)
square (5 + 4)
⇒ let x = 5 + 4 in square x
⇒ x * x
⇒ let x = 7 in x * x
⇒ 7*7
⇒ 49
(5,square (5 + 4))
⇒ let a = 5, b = square (5 + 4) in (a,b)
Given
fst (a, b) = a
fst (0, square (5 + 4))
Eager
fst (0, square (5 + 4))
⇒ fst (0, square 9)
⇒ fst (0, 9 * 9)
⇒ 0
Lazy
fst (0, square (5 + 4))
⇒ let a = 0, b = square (5 + 4) in fst (a, b)
⇒ a
⇒ 0
Lazy never evaluated square (5 + 4)
where eager did.
Given
fst (0, ⊥)
Eager
fst (0, ⊥)
⇒ fst ⊥
⇒ ⊥
Lazy
fst (0, ⊥)
⇒ let a = 0, b = ⊥ in fst (a, b)
⇒ a
⇒ 0
(f . g) input == f (g input)
g
only consumes input
as f
needs it.f
knows nothing of g
g
knows nothing of f
f
terminates g
terminates-- Generate a sqrt sequence of operations using Newton-Raphson
nextSqrtApprox n x = (x + n/x) / 2
-- iterate f x == [x, f x, f (f x), ...]
sqrtApprox n = iterate (nextSqrtApprox n) (n/2)
-- element where the difference with previous is below threshold
within eps (a:b:bs) | abs(a-b) <= eps = b
| otherwise = within eps (b:bs)
-- Calculate approximate sqrt using within
withinSqrt eps n = within eps (sqrtApprox n)
-- element where ratio with previous is close to 1
relative eps (a:b:bs) | abs(a-b) <= eps * abs b = b
| otherwise = relative eps (b:bs)
-- Calculate approximate sqrt using relative
relativeSqrt eps n = within eps (sqrtApprox n)
fib_list :: [Integer]
fib_list = 0:1:1:2:[n2 + n1| (n2, n1) <-
zip (drop 2 fib_list) (drop 3 fib_list)]
fib_best :: Int -> Integer
fib_best n = fib_list !! n
GHCi with timing
*Main> 5 < fib_best 100000
True
(0.86 secs, 449258648 bytes)
*Main> 5 < fib_best 100001
True
(0.02 secs, 0 bytes)
*Main>
Fibber
is some Num
type you can do math on.fib_worst :: Int -> Integer
fib_worst 0 = 0
fib_worst 1 = 1
fib_worst 2 = 1
fib_worst n = fib_worst(n-2) + fib_worst(n-1)
data Fibber = Fibber{fibNum :: Int, fibValue :: Integer}
makeFibber :: Int -> Fibber
makeFibber a = Fibber a (fib_worst a)
instance Eq Fibber where a == b = fibNum a == fibNum b
instance Ord Fibber where a <= b = fibNum a <= fibNum b
instance Show Fibber where show a = show . fibNum $ a
instance Num Fibber where
a + b = makeFibber (fibNum a + fibNum b)
a * b = makeFibber (fibNum a * fibNum b)
abs = makeFibber . abs . fibNum
signum = makeFibber . signum . fibNum
fromInteger = makeFibber . fromInteger
negate = makeFibber . negate . fibNum
GHCi
*Main> let fibber30 = makeFibber 30
(0.00 secs, 0 bytes)
*Main> let fibber25 = makeFibber 25
(0.00 secs, 0 bytes)
*Main> fibValue (fibber30 - fibber25)
5
(0.00 secs, 0 bytes)
*Main> fibValue fibber30
832040
(1.22 secs, 127472744 bytes)
*Main> fibValue fibber30
832040
(0.00 secs, 0 bytes)
*Main> fibValue fibber25
75025
(0.11 secs, 10684624 bytes)
*Main>
Some space leak examples from [Leaking Space - Eliminating memory hogs][8]
xs = delete "dead" ["alive", "dead"]
dead
alive until evaluation of xs
is forced.One form of space leak results from adding to and removing from lists but never evaluating (to reduce).
xs = let xs' = delete "dead" ["alive", "dead"] in xs' `seq` xs'
sum [1..n]
will consume O(n) space when evaluated strictly.sum [1..n]
should consume O(1) space when evaluated lazily (implementation).Accumulates (+)
operations.
sum1 (x:xs) = x + sum1 xs
sum1 [] = 0
Also accumulates (+)
operations.
sum2 xs = sum2’ 0 xs
where
sum2’ a (x:xs) = sum2’ (a+x) xs
sum2’ a [] = a
This definition is O(1) space.
sum3 xs = sum3’ 0 xs
where
sum3’ !a (x:xs) = sum3’ (a+x) xs
sum3’ !a [] = a
sum2
may be transformed into sum3
during strictness analysis.The article has more examples of space leaks. [8]: http://queue.acm.org/detail.cfm?id=2538488 (Leaking Space - Eliminating memory hogs: By Neil Mitchell)
-- Strict
splitAt_sp n xs = ("splitAt_lp " ++ show n) $
if n<=0
then ([], xs)
else
case xs of
[] -> ([], [])
y:ys ->
case splitAt_lp' (n-1) ys of
-- pattern match is strict
(prefix, suffix) -> (y : prefix, suffix)
-- Lazy
splitAt_lp n xs = ("splitAt_lp " ++ show n) $
if n<=0
then ([], xs)
else
case xs of
[] -> ([], [])
y:ys ->
case splitAt_lp' (n-1) ys of
-- pattern match is lazy
~(prefix, suffix) -> (y : prefix, suffix)
GHCi
*Main> sum . take 5 . fst . splitAt_sp 10000000 $ repeat 1
5
(20.78 secs, 3642437376 bytes)
*Main> sum . take 5 . fst . splitAt_lp 10000000 $ repeat 1
5
(0.00 secs, 0 bytes)