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
Final answer == expression in normal form
49
is the normal form of square (5 + 4 - 2)
Eager also called strict.
Lazy also called non-strict (sort of)
(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)
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
Lazy evaluation will keep 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).
sum [1..n]
will consume O(n) space when evaluated
strictly.sum [1..n]
should consume O(1) space
when evaluated lazily (implementation).This definition is O(n) space.
List is not actually kept in memory
Accumulates (+)
operations.
This definition is O(n) space.
Also accumulates (+)
operations.
This definition is O(1) space.
With optimizations in GHC sum2
may be transformed into
sum3
during strictness analysis.
The article has more examples of space leaks.