в конце для возврата пары
>(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]
Результат аналогичен тому, что был возвращён нашим предыдущим генератором списка. Как функция