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

очень похожа на композицию, но вместо того чтобы работать с обычными функциями типа >a –> b, она работает с монадическими функциями типа >a –> m b. Вот пример:

>ghci> let f = (+1) . (*100)

>ghci> f 4

>401

>ghci> let g = (\x –> return (x+1)) <=< (\x –> return (x*100))

>ghci> Just 4 >>= g

>Just 401

В данном примере мы сначала произвели композицию двух обычных функций, применили результирующую функцию к >4, а затем произвели композицию двух монадических функций и передали результирующей функции >Just 4 с использованием операции >>>=.

Если у вас есть набор функций в списке, вы можете скомпоновать их все в одну большую функцию, просто используя константную функцию >id в качестве исходного аккумулятора и функцию >(.) в качестве бинарной. Вот пример:

>ghci> letf = foldr (.) id [(+1),(*100),(+1)]

>ghci> f 1

>201

Функция >f принимает число, а затем прибавляет к нему >1, умножает результат на >100 и прибавляет к этому >1.

Мы можем компоновать монадические функции так же, но вместо обычной композиции используем операцию ><=<, а вместо >id – функцию >return. Нам не требуется использовать функцию >foldM вместо >foldr или что-то вроде того, потому что функция ><=< гарантирует, что композиция будет происходить монадически.

Когда вы знакомились со списковой монадой в главе 13, мы использовали её, чтобы выяснить, может ли конь пройти из одной позиции на шахматной доске на другую ровно в три хода. Мы создали функцию под названием >moveKnight, которая берёт позицию коня на доске и возвращает все ходы, которые он может сделать в дальнейшем. Затем, чтобы произвести все возможные позиции, в которых он может оказаться после выполнения трёх ходов, мы создали следующую функцию:

>in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight

И чтобы проверить, может ли конь пройти от >start до >end в три хода, мы сделали следующее:

>canReachIn3 :: KnightPos –> KnightPos –> Bool

>canReachIn3 start end = end `elem` in3 start

Используя композицию монадических функций, можно создать функцию вроде >in3, только вместо произведения всех позиций, которые может занимать конь после совершения трёх ходов, мы сможем сделать это для произвольного количества ходов. Если вы посмотрите на >in3, то увидите, что мы использовали нашу функцию >moveKnight трижды, причём каждый раз применяли операцию >>>=, чтобы передать ей все возможные предшествующие позиции. А теперь давайте сделаем её более общей. Вот так:

>import Data.List


>inMany :: Int –> KnightPos –> [KnightPos]

>inMany x start = return start >>= foldr (<=<) return (replicate x moveKnight)

Во-первых, мы используем функцию