Это из-за того, как работает правильный сгиб, да.Функция сгиба является накоплением: на каждом шаге, учитывая один элемент (в данном случае ключ и значение) и накопленный результат остальных данных, он объединяет их в один результат.Сгиб в целом делает это рекурсивно, чтобы суммировать весь набор данных.
В вашем случае вы отбрасываете ввод "результата" функции-накопителя - обратите внимание, что аргумент b
никогда не бываетиспользуемый.Имейте в виду, что IO a
- это не просто значение типа a
с добавленным дополнительным мусором, оно фактически представляет собой вычисление для получения a
, и это вычисление будет выполняться только путем объединения его с другимивычисления как часть окончательного значения функции main
(или, в GHCi, выражения, которое вычисляется).
Сбрасывая накопленное значение, другие вычисления никогда не становятся частью окончательного результата,и поэтому значения никогда не будут напечатаны.
Судя по тому, как вы сформулировали вопрос, я предполагаю, что вам все еще удобнее программирование в императивном стиле, чем в функциональном стиле Haskell.Очевидно, что в императивном языке, где печать фактически «происходит» во время сгиба в каком-то значимом смысле, было бы разумным предположить, что накопленная стоимость бесполезна.Если это поможет, подумайте об этом скорее как о метапрограммировании;вы на самом деле не пишете цикл для печати значений, вы создаете императивную программу, которая выполняет фактическую печать, и, отбрасывая накопленное значение, вы в основном отбрасываете все, кроме первых строк развернутого цикла, чтобы злоупотреблятьплохая аналогия.
В любом случае, в данном случае вам, вероятно, понадобится действие «напечатать остальные данные», параметр b
, и объединить его с действием putStrLn ...
, используя(>>)
, оператор, который в основном означает «выполнить первое действие, проигнорировать результат, выполнить второе».Это довольно прямой перевод императивного стиля «оператор печати в цикле».
Кроме того, хотя я понимаю, что это совершенно не относится к делу, я бы, вероятно, избегал смешивать форматирование и печать, которыетак или иначе.На мой взгляд, лучше форматировать каждую пару ключ / значение отдельно в список, а затем просто mapM_ putStrLn
.
mapM_
- это функция высшего порядка, которая описывает суть того, что вы здесь делаете;учитывая список некоторого типа a
и функцию, которая превращает a
в какое-то действие IO
, она применяет функцию к каждому элементу и выполняет результирующий список действий по порядку.mapM_
имеет тип Monad m => (a -> m b) -> [a] -> m ()
, который на первый взгляд кажется загадочным, но одна из приятных особенностей Haskell заключается в том, что как только вы привыкнете читать сигнатуры типов, тип mapM_
не только понятен с первого взгляда, но и сам по себе-документирование в том, что для функции с этим типом есть только одна разумная вещь, и именно это делает mapM_
.