Haskell Strictness Analysis
Strictness analysis is the compiler working out which values a function always needs. Values it always needs can be evaluated early instead of held as unevaluated thunks.
A bang pattern forces the accumulator at once
The accumulator carries an exclamation mark. Each step forces the running total before the next step.
{-# LANGUAGE BangPatterns #-}
strictSum :: Int -> [Int] -> Int
strictSum !acc [] = acc
strictSum !acc (x:xs) = strictSum (acc + x) xs
main :: IO ()
main = print (strictSum 0 [1..100])Seq forces a value before it is used
Seq demands its first argument. The doubled result is printed only after the input is forced.
strictDouble :: Int -> Int
strictDouble x = x `seq` (x * 2)
main :: IO ()
main = print (strictDouble 21)A strict field is evaluated when built
The field carries an exclamation mark. Unboxing the value returns the already evaluated number.
{-# LANGUAGE BangPatterns #-}
data Box = Box !Int
unbox :: Box -> Int
unbox (Box n) = n
main :: IO ()
main = print (unbox (Box (2 + 3)))Example
import Data.List (foldl')
main :: IO ()
main = do
let xs = [1..1000]
print (length xs)
print (foldl' (+) 0 xs)Key points
- Strictness analysis: the compiler finding values a function always needs.
- Thunk: an unevaluated expression waiting for its value to be demanded.
- Bang pattern: a pattern marked with an exclamation mark that forces its value at once.
- Seq: a function that forces its first argument before returning its second.
- Strict field: a record or constructor field marked to be evaluated when the value is built.
In this example, the program builds the numbers from 1 to 1000, prints the length of the list, then folds it with the strict foldl' and prints the total.
Note
strictness changes when work happens, and it never changes a correct result.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
What does a bang pattern on an accumulator do?