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