GHC does not perform this optimization because it is not always an optimization space-wise.
For instance, consider
n = 1000000
x = (length $ map succ [1..n], length $ map pred [1..n])
On a lazy language such as Haskell, one would expect this to run in constant space. Indeed, the list generating expression [1..n]
should lazily produce an element at a time, which would be affected by succ
/pred
because of the map
s, and then counted by length
. (Even better, succ
and pred
are not computed at all since length
does not force the list elements). After this, the produced element can be garbage collected, and the list generator can produce the next element, and so on. In real implementations, one would not expect every single element to be garbage collected immediately, but if the garbage collector is good, only a constant amount of them should be in memory at any time.
By comparison, the "optimized" code
n = 1000000
l = [1..n]
x = (length $ map succ l, length $ map pred l)
does not allow to garbage collect the elements of l
until both components of x
are evaluated. So, while it produces the list only once, it uses O(n
) words of memory to store the full list. This is likely to lead to a lower performance than the unoptimized code.