Ответ 1
Это из-за того, как работает правая складка, да. Функция сгиба - это накопление: на каждом шаге, учитывая один элемент (в данном случае ключ и значение) и накопленный результат остальной части данных, он объединяет их в один результат. Сложение в целом делает это рекурсивно, чтобы суммировать весь набор данных.
В вашем случае вы отбрасываете "результат" функции аккумулятора - обратите внимание, что аргумент 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_
.