This is a feature of the GHC compiler. Basically, GHC can recognize when a list is used in a "pipeline", and can convert the whole construct into the equivalent of a
while-loop in C that doesn't allocate a list at all.
The reason why this works with
foldr and not
foldl depends on the function
g that you're using in your example. Since
foldr, as opposed to
foldl, accumulates the results of the function given as the parameter (aka:
foldl needs the whole list before it can begin actually evaluating the function
g, so it builds up a huge "thunk" of unevaluated functions and the final element in the list as its result - which is why it uses so much more memory in this case - while
foldr can begin evaluating
g as soon as it gets any list input), it is called "strict" in its accumulator, and certain assumptions can be made by the compiler that can lead to optimizations.
If, for instance, the function
g yields a value that is a list, it can continue the aforementioned "pipeline" optimization strategy, basically treating the
foldr like a
map and making the whole construct (from list generation to list consumption) into a strict loop. This is only possible because the
foldr yields exactly one list element for every list element it consumes, which
foldl is not guaranteed to do (especially for infinite lists).