Why does foldl' use a lot of RAM with complex data structures? - haskell

Lazy fold uses a lot of RAM. In Data.List, foldl' provides a left fold that uses strict evaluation. For example, the following computes the sum of 10 million zeros with little increase in RAM usage.
sum0 = foldl' (+) 0 (replicate 10000000 0)
However, this does not seem to hold with complex data structures. For example, if we define addition for pairs of numbers and compute the sum of zero pairs, there is significant increase in RAM usage:
(x1,y1) <+> (x2,y2) = (x1 + x2,y1 + y2)
sum00 = foldl' (<+>) (0,0) (replicate 10000000 (0,0))
Why does that happen? Is there a way to decrease RAM usage?

foldl' only evaluates the intermediate state to weak head normal form—i.e. up to the first constructor. That's the most a generic function can do, and what functions that are called "strict" generally do. Evaluating (x1, y1) <+> (x2, y2) until it looks like a constructor gives (x1 + x2, y1 + y2), where the parts are still unevaluated (they have been "protected" by the (,)). Through the iteration, foldl' being strict keeps the state in the form (_, _) instead of (_, _) <+> (_, _) <+> ..., but the _s grow into huge unevaluated terms of form _ + _ + _ + ....
Modify <+> to evaluate the additions before it exposes the constructor.
(x1, y1) <+> (x2, y2) = x `seq` y `seq` (x, y)
where x = x1 + x2; y = y1 + y2
-- or
(x1, y1) <+> (x2, y2) = ((,) $! x1 + y1) $! x2 + y2
-- or (with deepseq package)
(x1, y1) <+> (x2, y2) = force (x1 + x2, y1 + y2)
-- x `seq` y = y, but only if x reaches WHNF
-- usually, evaluating x `seq` y to WHNF evaluates x (to WHNF) before it returns the result of evaluating y to WHNF
-- though that's not the official definition of `seq`, since Haskell nominally doesn't have an evaluation strategy
-- (and GHC's actual `seq` may do something different if GHC is feeling smart)

Related

Alpha equivalence between variables in lambda calculus

Just a fairly simple question (so it seems to me). If two variables (x)(x) are alpha equivalent. Is (x1x2)(x2x1) alpha equivalent?
Two terms are alpha-equivalent iff one can be converted into the other purely by renaming bound variables.
A variable is considered to be a bound variable if it matches the parameter name of some enclosing lambda. Otherwise it's a free variable. Here are a few examples:
λx. x -- x is bound
λx. y -- y is free
λf. λx. f x y -- f and x are bound, y is free
f (λf. f x) -- the first f is free; the second is bound. x is free
z -- z is free
Basically, "bound" and "free" roughly correspond to the notions of "in scope" and "out of scope" in procedural languages.
Alpha-equivalence basically captures the idea that it's safe to rename a variable in a program if you also fix all the references to that variable. That is, when you change the parameter of a lambda term, you also have to go into the lambda's body and change the usages of that variable. (If the name is re-bound by another lambda inside the first lambda, you'd better make sure not to perform the renaming inside the inner lambda.)
Here are some examples of alpha-equivalent terms:
λx. x <-> λy. y <-> λberp. berp
λx. λf. f x <-> λx. λg. g x <-> λf. λx. x f <-> λx1. λx2. x2 x1
λf. λf. f f <-> λg. λf. f f <-> λf. λg. g g
So is x x alpha-equivalent to x1x2 x1x2? No! x is free in the first term, because it's not bound by an enclosing lambda. (Perhaps it's a reference to a global variable.) So it's not safe to rename it to x1x2.
I suspect your tutor really meant to say that λx. x x is alpha-equivalent to λx1x2. x1x2 x1x2. Here the x is bound by the lambda, so you can safely rename it.
Is x1 x2 alpha-equivalent to x2 x1? For the same reason, no.
And is λx1. λx2. x1 x2 equivalent to λx1. λx2. x2 x1? Again, no, because this isn't just a renaming - the x1 and x2 variables moved around.
However, λx1. λx2. x1 x2 is alpha-equivalent to λx2. λx1. x2 x1:
rename x1 to some temporary name like z: λz. λx2. z x2
rename x2 to x1: λz. λx1. z x1
rename z back to x2: λx2. λx1. x2 x1
Getting renaming right in a language implementation is a fiddly enough problem that many compiler writers opt for a nameless representation of terms called de Bruijn indices. Rather than using text, variables are represented as a number measuring how many lambdas away the variable was bound. A nameless representation of λx2. λx1. x2 x1 would look like λ. λ. 2 1. Note that that's exactly the same as the de Bruijn representation of λx1. λx2. x1 x2. de Bruijn indices thoroughly solve the problem of alpha-equivalence (although they are quite hard to read).

Why is evaluating keys to WHNFs enough to construct Sets in Haskell?

Haskell's Data.Set page says:
Key arguments are evaluated to WHNF
in its "Strictness properties" section.
However, I wonder why WHNF is enough to construct Sets. For example, to construct an instance of Set [Int], we must evaluate its elements of Lists of Ints deeply to compare them.
To expand on #chi’s comment: This only means that keys are at least evaluated to WHNF. Often, they might be evaluated always once they are compared, but not always. Let’s invoke ghc-heap-view and look at a few examples:
Prelimaries:
~ $ ghci
GHCi, version 8.0.1: http://www.haskell.org/ghc/ :? for help
Prelude> :script /home/jojo/.cabal/share/x86_64-linux-ghc-8.0.1/ghc-heap-view-0.5.7/ghci
Prelude> import qualified Data.Set as S
singleton does not fully evaluate its argument (the _bco is a thunk):
Prelude S> let t = [True, True && True] -- a thunk in a list
Prelude S> let s1 = S.singleton t
Prelude S> s1 `seq` ()
()
Prelude S> :printHeap s1
let x1 = True()
x2 = Tip()
in Bin [x1,(_bco _fun)()] x2 x2 1
And even inserting another element will not fully evaluate the second elements in the list, as they can be distinguished already by looking at the first element:
Prelude S> let t2 = [False, False && False] -- a thunk in another list
Prelude S> let s2 = S.insert t2 s1
Prelude S> s2 `seq` ()
()
Prelude S> :printHeap s2
let x1 = True()
x2 = toArray (0 words)
f1 = _fun
x3 = []
x4 = False()
x5 = Tip()
in Bin (x1 : (_bco f1)() : x3) (Bin (x4 : (_bco f1)() : x3) x5 x5 1) x5 2
But inserting t2 again will now force the second element of that list:
Prelude S> let s3 = S.insert t2 s2
Prelude S> s3 `seq` ()
()
Prelude S> :printHeap s3
let x1 = True()
x2 = []
x3 = False()
x4 = Tip()
in Bin (x1 : (_bco _fun)() : x2) (Bin (x3 : _bh x3 : x2) x4 x4 1) x4 2
So you cannot rely on Data.Set to evaluate the keys fully as you store them. If you want that, you need to use, for example, (singleton $!! t1) and (insert $!! t2).
(If someone wants to replace the ghc-heap-view output in this answer with ghc-vis graphs, feel free to do so :-)).
There may be data types usable as keys that don't need to evaluate everything they contain to determine equality or order. Lists are no such types, of course.
However, while lists must be fully evaluated to find equality, they don't necessary need to in order to find non-equality. That is, we wouldn't expect that
[1..] == [2..]
computes forever. Likewise with
[] == [(40+2)..]
Here it is enough to get WHNF of the second list to find that it is not equal to the first one. We need not bother to compute 40+2, much less the succeeding elements.

Recursion and parallelism in Haskell

I'm trying to understand how paralleling in Haskell works and I've found following example in Control.Parallel docs.
import Control.Parallel
-- Equation for the upper hemisphere of the unit circle
circle :: Double -> Double
circle x = sqrt (abs(1 - x^2))
-- Calculate the area of a right-handed Riemann rectangle
area :: Double -> Double -> Double
area x1 x2 = (x2 - x1) * circle x2
-- Recursively add the areas of the Riemann rectangles
parEstimate :: [Double] -> Double
parEstimate (x:[]) = 0
parEstimate (x:y:[]) = area x y
parEstimate (x:y:xs) =
smaller `par` (larger `pseq` smaller + larger)
where smaller = area x y
larger = parEstimate (y:xs)
But I couldn't find an explanation of how this recursion works: parEstimate (x:y:xs), cause all examples I've found contains only two arguments.
That's why I cannot find out how to run this function. That's how I do:
main = print (parEstimate [1.0, 2.0])
but not sure, if it's correct.
Also I would like to implement function calculating definite integral based on this example.
The recursion, essentially, is a simple fold-like recursion scheme; if this were purely sequential, you might write it as
seqEstimate :: [Double] -> Double
seqEstimate (x:[]) = 0
seqEstimate (x:y:[]) = area x y
seqEstimate (x:y:xs) = smaller + larger
where smaller = area x y
larger = seqEstimate (y:xs)
(In fact, you would probably just use zipWith instead: seqEstimate xs = sum (zipWith area xs (tail xs)).)
The parallelized version is similar. This time, though, par is used to indicate that the left-hand side (smaller) can be evaluated in parallel with the right-hand side (pseq larger (smaller + larger)). Whether or not the compiler chooses to do so, and regardless of whether smaller completes before or after larger, the sum of smaller + larger will be correctly computed.

The function is applied with three arguments

I need to create a list of tuples, each tuple represents a rectangle with (x, y, height,width).
With width constant, and I need to double the value of the height.
The output necessary:
> genRects 3 (0,0)
[(0.0,0.0,5.5,5.5),(5.5,0.0,5.5,5.5),(11.0,0.0,5.5,5.5),(16.5,0.0,5.5,5.5)]
My Currently code:
genRects :: Int -> (Int,Int) -> [(Float,Float,Float,Float)]
genRects 0 _ = []
genRects n (x,y) = let height=5.5; width=5.5 in [(fromIntegral x, fromIntegral y, height, width)] genRects (n-1) (x+height, y)
Getting error:
Couldn't match expected type `(Int->(Int, Int)-> [(Float, Float, Float, Float)])
-> Int -> (Int, Int) -> [(Float, Float, Float, Float)]'
with actual type `[(Integer, Integer, Double, Double)]'
The function `[(fromIntegral x, fromIntegral y, height, width)]'
is applied to three arguments,
but its type `[(Integer, Integer, Double, Double)]' has none
In the expression:
[(fromIntegral x, fromIntegral y, altura, comp)]
genRects (n - 1) (x + x, y)
In the expression:
let
height= 5.5
width = 5.5
in
[(fromIntegral x, fromIntegral y, height, width)]
genRects (n - 1) (x + x, y)
Failed, modules loaded: none.
Also why is it going to double instead of Float?
Have a look at
[(fromIntegral x, fromIntegral y, height, width)] genRects (n-1) (x+height, y)
That doesn't make sense. What you are trying to do is create one of the rectangles, and cons it on to all of the other rectangles, so you should use something like
(fromIntegral x, fromIntegral y, height, width): genRects (n-1) (x+height, y)
However, after making this change, you will see that
Couldn't match expected type `Int' with actual type `Float'
In the second argument of `(+)', namely `height'
In the expression: x + height
In the second argument of `genRects', namely `(x + height, y)'
Which makes sense, you are trying to add a Float (5.5) to the x value, which you have said is an Int. The simplest solution is to use Floats for x,y, instead of Ints.
So you can change your code to,
genRects :: Int -> (Float,Float) -> [(Float,Float,Float,Float)]
genRects 0 _ = []
genRects n (x,y) = let height=5.5; width=5.5 in (x, y, height, width): genRects (n-1) (x+height, y)
And get the desired result.
Any time you have a function which creates n of something where n is an explicit parameter to the function, ask yourself if you could instead write it as:
take n $ some_infinite_list
where some_infinite_list is an expression which creates an infinite list of the somethings.
For instance, here is the pattern of calls created by genRects n (x,y):
genRects n (x,y)
calls genRects (n-1) (x+h,y) -- h = height
calls genRects (n-2) (x+h+h, y)
calls genRects (n-3) (x+h+h+h, y)
...
So if we start with the sequence:
xys = [ (x,y), (x+h, y), (x+h+h, y), (x+3*h, y), ... ]
and create a function to4Tuple:
to4Tuple :: (Int,Int) -> (Float,Float,Float,Float)
mapping a pair (x,y) to your 4-tuple of floating point numbers, we can write genRects as:
genRects n (x0,y0) = take n $ map to4Tuple sys
where xys = [ (x,y0) | x <- [x0, x0+h .. ] ]
to4Tuple (x,y) = ...construct 4-tuple from x,y here...
Now you've avoided explicit recursion in the definition of genRects.
In
[(fromIntegral x, fromIntegral y, height, width)] genRects (n - 1) (x + x, y)
you are applying a list to three arguments. Lists are not functions and cannot be applied like that. GHC is talking about Double because you've confused it and when it doesn't know what sort of floating point it's supposed to be dealing with, it defaults to Double.
Side note: unless you're dealing with algorithms specialty tuned for single precision floating point, or dealing with special library functions that require such, or using unboxed arrays of them to save on space, you should really be using Double in your code.

What's wrong with my Graham Scan function?

I almost finish the chapter 3 of Real World Haskell. The last exercise blocks me. My code will crash while running. Does anyone can tell me which part is wrong in my code? Thanks.
Question:
Using the code from the preceding three exercises, implement Graham's scan algorithm for the convex hull of a set of 2D points. You can find good description of what a convex hull is, and how the Graham scan algorithm should work, on Wikipedia.
Answer:
-- Graham Scan, get the convex hull.
-- Implement the algorithm on http://en.wikipedia.org/wiki/Graham_scan
import Data.List
import Data.Ord
data Direction = TurnLeft | TurnRight | GoStraight deriving (Eq, Show)
-- Determine if three points constitute a "left turn" or "right turn" or "go straight".
-- For three points (x1,y1), (x2,y2) and (x3,y3), simply compute the direction of the cross product of the two vectors defined by points (x1,y1), (x2,y2) and (x1,y1), (x3,y3), characterized by the sign of the expression (x2 − x1)(y3 − y1) − (y2 − y1)(x3 − x1). If the result is 0, the points are collinear; if it is positive, the three points constitute a "left turn", otherwise a "right turn".
direction a b c = case compare ((x2 - x1) * (y3 - y1)) ((y2 - y1) * (x3 - x1)) of
EQ -> GoStraight
GT -> TurnLeft
LT -> TurnRight
where x1 = fst a
y1 = snd a
x2 = fst b
y2 = snd b
x3 = fst c
y3 = snd c
grahamScan points = scan (sort points)
-- For each point, it is determined whether moving from the two previously considered points to this point is a "left turn" or a "right turn". If it is a "right turn", this means that the second-to-last point is not part of the convex hull and should be removed from consideration. This process is continued for as long as the set of the last three points is a "right turn". As soon as a "left turn" is encountered, the algorithm moves on to the next point in the sorted array. (If at any stage the three points are collinear, one may opt either to discard or to report it, since in some applications it is required to find all points on the boundary of the convex hull.)
where scan (a : (b : (c : points)))
| (direction a b c) == TurnRight = scan (a : (c : points))
| otherwise = a : (scan (b : (c : points)))
scan (a : (b : [])) = []
-- Put prime to the head.
sort points = prime : (reverse (sortBy (compareByCosine prime) rest))
-- Sort a list to compute. The first step is to find a point whose y-coordinate is lowest. If there are more than one points with lowest y-coordinate, take the one whose x-coordinate is lowest. I name it prime.
where compareByYAndX a b
| compareByY == EQ = comparing fst a b
| otherwise = compareByY
where compareByY = comparing snd a b
prime = minimumBy compareByYAndX points
-- Delete prime from the candidate points. Sort the rest part by the cosine value of the angle between each vector from prime to each point. Reverse it and put prime to the head.
rest = delete prime points
compareByCosine p a b = comparing cosine pa pb
where pa = (fst a - fst p, snd a - snd p)
pb = (fst b - fst p, snd b - snd p)
cosine v = x / sqrt (x ^ 2 + y ^ 2)
where x = fst v
y = snd v
When doing the scan on the sorted set of points, when you finish, you throw away points that are supposed to be in the hull. In this line
scan (a : (b : [])) = []
you are deleting the last two points in the hull. Really it should be
scan (a : (b : [])) = [a, b]
or even better,
scan ps = ps
which covers cases with only one point.

Resources