Но что это за [(),(),(),()]
в конце вывода? При выполнении в GHCi действия ввода-вывода помимо самого действия выводится результат выполнения, но только если этот результат не есть ()
. Поэтому при выполнении в GHCi putStrLn "ха-ха"
просто выводится строка – результатом является ()
. Если же попробовать ввести getLine
, то помимо собственно ввода с клавиатуры будет выведено введённое значение – результатом является IO
String
.
Функция mapM
Поскольку применение функции, возвращающей действие ввода-вывода, к элементам списка и последующее выполнение всех полученных действий очень распространено, для этих целей были введены две вспомогательные функции – mapM
и mapM_
. Функция mapM
принимает функцию и список, применяет функцию к элементам списка, сводит элементы в одно действие ввода-вывода и выполняет их. Функция mapM_
работает так же, но отбрасывает результат действия ввода-вывода. Она используется, когда нам не важен результат комбинированного действия ввода-вывода.
ghci> mapM print [1,2,3]
1
2
3
[(),(),()]
ghci> mapM_ print [1,2,3]
1
2
3
Функция forever
Функция forever
принимает действие ввода-вывода – параметр и возвращает действие ввода-вывода – результат. Действие-результат будет повторять действие-параметр вечно. Эта функция входит в модуль Control.Monad
. Следующая программа будет бесконечно спрашивать у пользователя строку и возвращать её в верхнем регистре:
import Control.Monad
import Data.Char
main = forever $ do
putStr "Введите что-нибудь: "
l <– getLine
putStrLn $ map toUpper l
Функция 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
.
Обзор системы ввода-вывода