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

в конце для возврата пары >(n,>ch) в качестве результата, потому что выводящая часть генератора списка сделала это за нас.

На самом деле генераторы списков являются просто синтаксическим сахаром для использования списков как монад. В конечном счёте генераторы списков и списки, используемые в нотации >do, переводятся в использование операции >>>= для осуществления вычислений, которые обладают недетерминированностью.

Класс MonadPlus и функция guard

Генераторы списков позволяют нам фильтровать наши выходные данные. Например, мы можем отфильтровать список чисел в поиске только тех из них, которые содержат цифру >7:

>ghci> [x | x <– [1..50], '7' `elem` show x]

>[7,17,27,37,47]

Мы применяем функцию >show к параметру >x чтобы превратить наше число в строку, а затем проверяем, является ли символ >'7' частью этой строки.

Чтобы увидеть, как фильтрация в генераторах списков преобразуется в списковую монаду, мы должны рассмотреть функцию >guard и класс типов >MonadPlus.

Класс типов >MonadPlus предназначен для монад, которые также могут вести себя как моноиды. Вот его определение:

>class Monad m => MonadPlus m where

>   mzero :: m a

>   mplus :: m a –> m a –> m a

Функция >mzero является синонимом функции >mempty из класса типов >Monoid, а функция >mplus соответствует функции >mappend. Поскольку списки являются моноидами, а также монадами, их можно сделать экземпляром этого класса типов:

>instance MonadPlus [] where

>   mzero = []

>   mplus = (++)

Для списков функция >mzero представляет недетерминированное вычисление, которое вообще не имеет результата – неуспешно окончившееся вычисление. Функция >mplus сводит два недетерминированных значения в одно. Функция >guard определена следующим образом:

>guard :: (MonadPlus m) => Bool –> m ()

>guard True = return ()

>guard False = mzero

Функция >guard принимает значение типа >Bool. Если это значение равно >True, функция >guard берёт пустой кортеж >() и помещает его в минимальный контекст, который по-прежнему является успешным. Если значение типа >Bool равно >False, функция >guard создаёт монадическое значение с неудачей в вычислениях. Вот эта функция в действии:

>ghci> guard (5 > 2) :: Maybe ()

>Just ()

>ghci> guard (1 > 2) :: Maybe ()

>Nothing

>ghci> guard (5 > 2) :: [()]

>[()]

>ghci> guard (1 > 2) :: [()]

>[]

Выглядит интересно, но чем это может быть полезно? В списковой монаде мы используем её для фильтрации недетерминированных вычислений:

>ghci> [1..50] >>= (\x –> guard ('7' `elem` show x) >> return x)

>[7,17,27,37,47]

Результат аналогичен тому, что был возвращён нашим предыдущим генератором списка. Как функция