Функция forM

Функция forM (определена в модуле Control.Monad) похожа на функцию mapM, но её параметры поменяны местами. Первый параметр – это список, второй – это функция, которую надо применить к списку и затем свести действия из списка в одно действие. Для чего это придумано? Если творчески использовать лямбда-выражения и ключевое слово do, можно проделывать такие фокусы:

import Control.Monad

main = do

   colors <– forM [1,2,3,4] (a –> do

      putStrLn $ "С каким цветом ассоциируется число "

                 ++ show a ++ "?"

      color <– getLine

      return color)

   putStrLn "Цвета, ассоциирующиеся с 1, 2, 3 и 4: "

   mapM putStrLn colors

Вот что мы получим при запуске:

С каким цветом ассоциируется число 1?

белый

С каким цветом ассоциируется число 2?

синий

С каким цветом ассоциируется число 3?

красный

С каким цветом ассоциируется число 4?

оранжевый

Цвета, ассоциирующиеся с 1, 2, 3 и 4:

белый

синий

красный

оранжевый

Анонимная функция (a –> do ...) – это функция, которая принимает число и возвращает действие ввода-вывода. Нам пришлось поместить её в скобки, иначе анонимная функция решит, что следующие два действия ввода-вывода принадлежат ей. Обратите внимание, что мы производим вызов return color внутри блока do. Это делается для того, чтобы действие ввода-вывода, возвращаемое блоком do, содержало в себе цвет. На самом деле мы не обязаны этого делать, потому что функция getLine уже содержит цвет внутри себя. Выполняя color <– getLine и затем return color, мы распаковываем результат getLine и затем запаковываем его обратно, то есть это то же самое, что просто вызвать функцию getLine. Функция forM (вызываемая с двумя параметрами) создаёт действие ввода-вывода, результат которого мы связываем с идентификатором colors. Этот идентификатор – обычный список, содержащий строки. В конце мы распечатываем все цвета, вызывая выражение mapM putStrLn colors.

Вы можете думать, что функция forM имеет следующий смысл: «Создай действие ввода-вывода для каждого элемента в списке. Каков будет результат каждого такого действия, может зависеть от элемента, из которого оно создаётся. После создания списка действий исполни их и привяжи их результаты к чему-либо». Однако мы не обязаны их связывать – результаты можно просто отбросить.

На самом деле мы могли бы сделать это без использования функции forM, но так легче читается. Обычно эта функция используется, когда нам нужно отобразить (map) и объединить (sequence) действия, которые мы тут же определяем в секции do. Таким образом, мы могли бы заменить последнюю строку на выражение forM colors putStrLn.