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

. Она является частью класса типов >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

Согласуется. Очень хорошо! Теперь мы хотим снабдить наш результат контекстом, а контекстом будет моноидное значение, которое ведёт себя как журнал. Как и прежде, мы используем список строк в качестве моноида. Поэтому тип нашей новой функции