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

>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"])

Добавление в программы функции журналирования

Иногда мы просто хотим, чтобы некое моноидное значение было включено в каком-то определённом месте. Для этого может пригодиться функция