Haskell Memory Patterns
A memory pattern is the shape of how a program holds values while it works. A lazy list held whole is a space leak, while a consumed stream or a strict fold keeps memory small.
Take consumes only the head of a list
Only the first hundred numbers are taken. The sum and the count describe just that head.
main :: IO ()
main = do
print (sum (take 100 [1..]))
print (length (take 100 [1..]))A strict fold summarises without holding thunks
The strict fold adds as it goes. The length confirms the whole list was covered.
import Data.List (foldl')
main :: IO ()
main = do
print (foldl' (+) 0 [1..100])
print (length [1..100])Chunks add up to the whole total
The list is summed in two chunks. The two chunk sums add to the total of the whole list.
main :: IO ()
main = do
let xs = [1..20]
print (sum (take 10 xs))
print (sum (drop 10 xs))
print (sum xs)Example
import Data.List (foldl')
main :: IO ()
main = do
let ws = ["red", "green", "blue", "gold"]
print (length ws)
print (foldl' (+) 0 (map length ws))Key points
- Space leak: memory held by values a program no longer needs.
- Consumed stream: a list processed piece by piece and then let go.
- Strict fold: a fold that forces its accumulator so no chain of thunks builds up.
- Use take to work with only the head of a long or endless list.
- Use a strict fold when only a summary of a list is needed.
In this example, the program lists four words, prints how many there are, then folds over their lengths with a strict fold and prints the total number of letters.
Note
this lesson proves its claims with counts and sums, never with a memory measurement.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
What shape should a program prefer when it only needs a summary of a long list?