Изучай Haskell во имя добра! (Липовача) - страница 190

функция >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 как коробку, которая выйдет в реальный мир и принесёт нам строку. Выполнение выражения