Это особенность компилятора GHC. По сути, GHC может распознать, когда список используется в «конвейере», и может преобразовать всю конструкцию в эквивалент while
-петля в C, который вообще не выделяет список.
Причина, по которой это работает с foldr
, а не foldl
, зависит от функции g
, которую вы используете в своем примере. Так как foldr
, в отличие от foldl
, накапливает результаты функции, заданной в качестве параметра (aka: foldl
нужен весь список, прежде чем он сможет начать фактически оценивать функцию g
, поэтому он создает огромный «поток» из неоцененных функций и конечный элемент списка в качестве своего результата - именно поэтому в этом случае он использует гораздо больше памяти - тогда как foldr
может начать вычислять g
, как только получает любой вход в список), он называется «строгим» в своем аккумуляторе, и компилятор может сделать некоторые предположения, которые могут привести к оптимизации.
Если, например, функция g
возвращает значение, являющееся списком, она может продолжить вышеупомянутую стратегию оптимизации "конвейера", в основном трактуя foldr
как map
и делая всю конструкцию (из генерация списка для списочного потребления) в строгом цикле. Это возможно только потому, что foldr
дает ровно один элемент списка для каждого потребляемого элемента списка, что не гарантируется для foldl
(особенно для бесконечных списков).