Besides looking at runtime profiling data, one sure way to determine exactly what your program is doing is to look at the final program source after the compiler is done optimizing it, particularly in the case of Haskell compilers, which can perform very aggressive transformations on the code. GHC uses what is humorously referred to as “a simple functional language”—known as Core—as the compiler intermediate representation. It is essentially a subset of Haskell, augmented with unboxed data types (raw machine types, directly corresponding to primitive data types in languages such as C), suitable for code generation. GHC optimizes Haskell by transformation, repeatedly rewriting the source into more and more efficient forms. The Core representation is the final functional version of your program, before translation to low-level imperative code. In other words, Core has the final say, and if all-out performance is your goal, it is worth understanding.
To view the Core version of our Haskell
program, we compile with the
-ddump-simpl flag, or use the
ghc-core tool, a third-party
utility that lets us view Core in a pager. So let’s look at the
representation of our final
fold using strict data types,
in Core form:
ghc -O2 -ddump-simpl G.hs
A screenful of text is generated. If we look carefully at it, we’ll see a loop (here, cleaned up slightly for clarity):
lgo :: Integer -> [Double] -> Double# -> (# Integer, Double #) lgo = \ n xs s -> case xs of  -> (# n, D# s #); (:) ...