. Она является частью класса типов
>MonadWriter
и в случае с типом
>Writer
берёт монадическое значение вроде
>["Всё продолжается"]
и создаёт значение типа
>Writer
, которое возвращает значение-пустышку
>()
в качестве своего результата, но прикрепляет желаемое моноидное значение. Когда у нас есть монадическое значение, которое в качестве результата содержит значение
>()
, мы не привязываем его к переменной. Вот определение функции
>multWithLog
с включением некоторых дополнительных сообщений:
>multWithLog :: Writer [String] Int
>multWithLog = do
> a <– logNumber 3
> b <– logNumber 5
> tell ["Перемножим эту парочку"]
> return (a*b)
Важно, что вызов >return (a*b)
находится в последней строке, потому что результат последней строки в выражении >do
является результатом всего выражения >do
. Если бы мы поместили вызов функции >tell
на последнюю строку, результатом этого выражения >do
было бы >()
. Мы бы потеряли результат умножения. Однако журнал остался бы прежним. Вот функция в действии:
>ghci> runWriter multWithLog
>(15,["Получено число: 3","Получено число: 5","Перемножим эту парочку"])
Добавление журналирования в программы
Алгоритм Евклида – это алгоритм, который берёт два числа и вычисляет их наибольший общий делитель, то есть самое большое число, на которое делятся без остатка оба числа. В языке Haskell уже имеется функция >gcd
, которая проделывает это, но давайте реализуем её сами, а затем снабдим её возможностями журналирования. Вот обычный алгоритм:
>gcd' :: Int –> Int –> Int
>gcd' a b
> | b == 0 = a
> | otherwise = gcd' b (a `mod` b)
Алгоритм очень прост. Сначала он проверяет, равно ли второе число 0. Если равно, то результатом становится первое число. Если не равно, то результатом становится наибольший общий делитель второго числа и остаток от деления первого числа на второе. Например, если мы хотим узнать, каков наибольший общий делитель 8 и 3, мы просто следуем изложенному алгоритму. Поскольку 3 не равно 0, мы должны найти наибольший общий делитель 3 и 2 (если мы разделим 8 на 3, остатком будет 2). Затем ищем наибольший общий делитель 3 и 2. Число 2 по-прежнему не равно 0, поэтому теперь у нас есть 2 и 1. Второе число не равно 0, и мы выполняем алгоритм ещё раз для 1 и 0, поскольку деление 2 на 1 даёт нам остаток равный 0. И наконец, поскольку второе число равно 0, финальным результатом становится 1. Давайте посмотрим, согласуется ли наш код:
>ghci> gcd' 8 3
>1
Согласуется. Очень хорошо! Теперь мы хотим снабдить наш результат контекстом, а контекстом будет моноидное значение, которое ведёт себя как журнал. Как и прежде, мы используем список строк в качестве моноида. Поэтому тип нашей новой функции