Итак, мы зарядили наш пистолет монадой >Maybe
, списковой монадой и монадой >IO
. Теперь давайте поместим в патронник монаду >Writer
и посмотрим, что произойдёт, когда мы выстрелим ею!
Между тем как >Maybe
предназначена для значений с добавленным контекстом неуспешно оканчивающихся вычислений, а список – для недетерминированных вычислений, монада >Writer
предусмотрена для значений, к которым присоединено другое значение, ведущее себя наподобие журнала. Монада >Writer
позволяет нам производить вычисления, в то же время обеспечивая слияние всех журнальных значений в одно, которое затем присоединяется к результату.
Например, мы могли бы снабдить наши значения строками, которые объясняют, что происходит, возможно, для отладочных целей. Рассмотрите функцию, которая принимает число бандитов в банде и сообщает нам, является ли эта банда крупной. Это очень простая функция:
>isBigGang :: Int –> Bool
>isBigGang x = x > 9
Ну а что если теперь вместо возвращения значения >True
или >False
мы хотим, чтобы функция также возвращала строку журнала, которая сообщает, что она сделала? Что ж, мы просто создаём эту строку и возвращаем её наряду с нашим значением >Bool
:
>isBigGang :: Int –> (Bool, String)
>isBigGang x = (x > 9, "Размер банды сравнён с 9.")
Так что теперь вместо того, чтобы просто вернуть значение типа >Bool
, мы возвращаем кортеж, первым компонентом которого является само значение, а вторым компонентом – строка, сопутствующая этому значению. Теперь у нашего значения появился некоторый добавленный контекст. Давайте опробуем функцию:
>ghci> isBigGang 3
>(False,"Размер банды сравнён с 9.")
>ghci> isBigGang 30
>(True,"Размер банды сравнён с 9.")
Пока всё нормально. Функция >isBigGang
принимает нормальное значение и возвращает значение с контекстом. Как мы только что увидели, передача ей нормального значения не составляет сложности. Теперь предположим, что у нас уже есть значение, у которого имеется журнальная запись, присоединённая к нему – такая как