ghci> let h = f <=< g
ghci> h 3
[9,-9,6,-6]
Ладно, всё это здорово. Но какое это имеет отношение к закону ассоциативности? Просто, когда мы рассматриваем этот закон как закон композиций, он утверждает, что f <=< (g <=< h)
(f <=< g) <=< h
. Это всего лишь ещё один способ доказать, что для монад вложенность операций не должна иметь значения.Если мы преобразуем первые два закона так, чтобы они использовали операцию <=<
f
выражение f <=< return
означает то же самое, что просто вызвать f
. Закон правого тождества говорит, что выражение return <=< f
также ничем не отличается от простого вызова f
. Это подобно тому, как если бы f
являлась обычной функцией, и тогда (f . g) . h
было бы аналогично f . (g . h)
, выражение f . id
– всегда аналогично f
, и выражение id
.
f
тоже ничем не отличалось бы от вызова f
.В этой главе мы в общих чертах ознакомились с монадами и изучили, как работают монада Maybe
14
Ещё немного монад
Мы видели, как монады могут быть использованы для получения значений с контекстами и применения их к функциям и как использование оператора >>=
do
позволяет нам сфокусироваться на самих значениях, в то время как контекст обрабатывается за нас.Мы познакомились с монадой Maybe
IO
даже до того, как вообще выяснили, что такое монада!В этой главе мы узнаем ещё о нескольких монадах. Мы увидим, как они могут сделать наши программы понятнее, позволяя нам обрабатывать все типы значений как монадические значения. Исследование ряда примеров также укрепит наше понимание монад.
Все монады, которые нам предстоит рассмотреть, являются частью пакета
ghc-pkg list
в командной строке. Эта команда покажет, какие пакеты для языка Haskell у вас уже установлены; одним из таких пакетов должен являться Writer? Я о ней почти не знаю!
Итак, мы зарядили наш пистолет монадой 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.")