функция
>pure
– это просто вызов
>return
. Функция
>return
создаёт действие ввода-вывода, которое ничего не делает. Оно просто возвращает некое значение в качестве своего результата, не производя никаких операций ввода-вывода вроде печати на терминал или чтения из файла.
Если бы оператор ><*>
ограничивался работой с типом >IO
, он бы имел тип >(<*>) :: IO (a –> b) –> IO a –> IO b
. В случае с типом >IO
он принимает действие ввода-вывода >a
, которое возвращает функцию, выполняет действие ввода-вывода и связывает эту функцию с идентификатором >f
. Затем он выполняет действие ввода-вывода >b
и связывает его результат с идентификатором >x
. Наконец, он применяет функцию >f
к значению >x
и возвращает результат этого применения в качестве результата. Чтобы это реализовать, мы использовали здесь синтаксис >do
. (Вспомните, что суть синтаксиса >do
заключается в том, чтобы взять несколько действий ввода-вывода и «склеить» их в одно.)
При использовании типов >Maybe
и >[]
мы могли бы воспринимать применение функции ><*>
просто как извлечение функции из её левого параметра, а затем применение её к правому параметру. В отношении типа >IO
извлечение остаётся в силе, но теперь у нас появляется понятие помещения в последовательность, поскольку мы берём два действия ввода-вывода и «склеиваем» их в одно. Мы должны извлечь функцию из первого действия ввода-вывода, но для того, чтобы можно было извлечь результат из действия ввода-вывода, последнее должно быть выполнено. Рассмотрите вот это:
>myAction :: IO String
>myAction = do
> a <– getLine
> b <– getLine
> return $ a ++ b
Это действие ввода-вывода, которое запросит у пользователя две строки и вернёт в качестве своего результата их конкатенацию. Мы достигли этого благодаря «склеиванию» двух действий ввода-вывода >getLine
и >return
, поскольку мы хотели, чтобы наше новое «склеенное» действие ввода-вывода содержало результат выполнения >a ++ b
. Ещё один способ записать это состоит в использовании аппликативного стиля:
>myAction :: IO String
>myAction = (++) <$> getLine <*> getLine
Это то же, что мы делали ранее, когда создавали действие ввода-вывода, которое применяло функцию между результатами двух других действий ввода-вывода. Вспомните, что функция >getLine
– это действие ввода-вывода, которое имеет тип >getLine :: IO String
. Когда мы применяем оператор ><*>
между двумя аппликативными значениями, результатом является аппликативное значение, так что всё это имеет смысл.
Если мы вернёмся к аналогии с коробками, то можем представить себе функцию >getLine
как коробку, которая выйдет в реальный мир и принесёт нам строку. Выполнение выражения