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

>[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]

>ghci> [[x,y,z] | x <– [1,2], y <– [3,4], z <– [5,6]]

>[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]

Выражение >(+) <$> [1,2] <*> [4,5,6] возвращает в результате недетерминированное вычисление >x>+>y, где образец >x принимает каждое значение из >[1,2], а >y принимает каждое значение из >[4,5,6]. Мы представляем это в виде списка, который содержит все возможные результаты. Аналогичным образом, когда мы выполняем выражение >sequenceA [[1,2],[3,4],[5,6]], результатом является недетерминированное вычисление >[x,y,z], где образец >x принимает каждое значение из >[1,2], а >y – каждое значение из >[3,4] и т. д. Для представления результата этого недетерминированного вычисления мы используем список, где каждый элемент в списке является одним возможным списком. Вот почему результатом является список списков.

При использовании с действиями ввода-вывода функция >sequenceA представляет собой то же самое, что и функция >sequence! Она принимает список действий ввода-вывода и возвращает действие ввода-вывода, которое выполнит каждое из этих действий и в качестве своего результата будет содержать список результатов этих действий ввода-вывода. Так происходит, потому что чтобы превратить значение >[IO a] в значение >IO [a], чтобы создать действие ввода-вывода, возвращающее список результатов при выполнении, все эти действия ввода-вывода должны быть помещены в последовательность, а затем быть выполненными одно за другим, когда потребуется результат выполнения. Вы не можете получить результат действия ввода-вывода, не выполнив его!

Давайте поместим три действия ввода-вывода >getLine в последовательность:

>ghci> sequenceA [getLine, getLine, getLine]

>эй

>хо

>ух

>["эй","хо","ух"]

В заключение отмечу, что аппликативные функторы не просто интересны, но и полезны. Они позволяют нам объединять разные вычисления – как, например, вычисления с использованием ввода-вывода, недетерминированные вычисления, вычисления, которые могли окончиться неуспешно, и т. д., – используя аппликативный стиль. Просто с помощью операторов ><$> и ><*> мы можем применять обычные функции, чтобы единообразно работать с любым количеством аппликативных функторов и использовать преимущества семантики каждого из них.

12

Моноиды

В этой главе представлен ещё один полезный и интересный класс типов >Monoid. Он существует для типов, значения которых могут быть объединены при помощи бинарной операции. Мы рассмотрим, что именно представляют собой моноиды и что утверждают их законы. Затем рассмотрим некоторые моноиды в языке Haskell и обсудим, как они могут нам пригодиться.