>ghci> runWriter (return 3 :: Writer String Int)
>(3,"")
>ghci> runWriter (return 3 :: Writer (Sum Int) Int)
>(3,Sum {getSum = 0})
>ghci> runWriter (return 3 :: Writer (Product Int) Int)
>(3,Product {getProduct = 1})
Поскольку у типа >Writer
нет экземпляра класса >Show
, нам пришлось использовать функцию >runWriter
для преобразования наших значений типа >Writer
в нормальные кортежи, которые могут быть показаны в виде строки. Для строк единичным значением является пустая строка. Для типа >Sum
это значение >0
, потому что если мы прибавляем к чему-то >0
, это что-то не изменяется. Для типа >Product
единичным значением является >1
.
В экземпляре класса >Monad
для типа >Writer
не имеется реализация для функции >fail
; значит, если сопоставление с образцом в нотации >do
оканчивается неудачно, вызывается функция >error
.
Использование нотации do с типом Writer
Теперь, когда у нас есть экземпляр класса >Monad
, мы свободно можем использовать нотацию >do
для значений типа >Writer
. Это удобно, когда у нас есть несколько значений типа >Writer
и мы хотим с ними что-либо делать. Как и в случае с другими монадами, можно обрабатывать их как нормальные значения, и контекст сохраняется для нас. В этом случае все моноидные значения, которые идут в присоединённом виде, объединяются с помощью функции >mappend
, а потому отражаются в окончательном результате. Вот простой пример использования нотации >do
с типом >Writer
для умножения двух чисел:
>import Control.Monad.Writer
>logNumber :: Int –> Writer [String] Int
>logNumber x = Writer (x, ["Получено число: " ++ show x])
>multWithLog :: Writer [String] Int
>multWithLog = do
> a <– logNumber 3
> b <– logNumber 5
> return (a*b)
Функция >logNumber
принимает число и создаёт из него значение типа >Writer
. Для моноида мы используем список строк и снабжаем число одноэлементным списком, который просто говорит, что мы получили это число. Функция >multWithLog
– это значение типа >Writer
, которое перемножает >3
и >5
и гарантирует включение прикреплённых к ним журналов в окончательный журнал. Мы используем функцию >return
, чтобы вернуть значение >(a*b)
в качестве результата. Поскольку функция >return
просто берёт что-то и помещает в минимальный контекст, мы можем быть уверены, что она ничего не добавит в журнал. Вот что мы увидим, если выполним этот код:
>ghci> runWriter multWithLog
>(15,["Получено число: 3","Получено число: 5"])
Добавление в программы функции журналирования
Иногда мы просто хотим, чтобы некое моноидное значение было включено в каком-то определённом месте. Для этого может пригодиться функция