Дейв Хинтон ответ верен, но в качестве отступления вот еще один способ написания того же кода:
import Control.Applicative
main = (sequence_ . proc) =<< (read <$> getLine)
proc x = replicate x (putStrLn =<< (findNextPalin <$> getLine))
Просто чтобы напомнить всем, что блоки do
не нужны! Обратите внимание, что в приведенном выше тексте =<<
и <$>
означают простое старое применение функции . Если вы игнорируете оба оператора, код читается точно так же, как это делают чистые функции с аналогичной структурой. Я добавил несколько бесплатных скобок, чтобы сделать вещи более явными.
Их целью является то, что <$>
применяет обычную функцию внутри монады, тогда как =<<
делает то же самое, но затем сжимает дополнительный слой монады (например, превращая IO (IO a)
в IO a
).
Интересная часть взгляда на код таким образом заключается в том, что вы в основном можете игнорировать, где находятся монады и тому подобное; как правило, существует очень мало способов разместить операторы «приложения функции», чтобы заставить типы работать.