Результаты оказались такими, каких мы и ждали! Если монадическое значение слева равно Nothing
, то всё будет равно Nothing
. А если функция справа возвращает значение Nothing
, результатом опять будет Nothing
. Это очень похоже на тот случай, когда мы использовали тип Maybe
в качестве аппликативного функтора и в результате получали значение Nothing
, если где-то в составе присутствовало значение Nothing
.
Похоже, мы догадались, как взять причудливое значение, передать его функции, которая принимает обычное значение, и вернуть причудливое значение. Мы сделали это, помня, что значение типа Maybe
представляет вычисление, которое могло окончиться неуспешно.
Вы можете спросить себя: «Чем это полезно?» Может показаться, что аппликативные функторы сильнее монад, поскольку аппликативные функторы позволяют нам взять обычную функцию и заставить её работать со значениями, имеющими контекст. В этой главе вы увидите, что монады, будучи усовершенствованными аппликативными функторами, тоже способны на такое. На самом деле они могут делать и кое-какие другие крутые вещи, на которые не способны аппликативные функторы.
Мы вернёмся к Maybe
через минуту, но сначала давайте взглянем на класс типов, который относится к монадам.
Класс типов Monad
Как и функторы, у которых есть класс типов Functor
, и аппликативные функторы, у которых есть класс типов Applicative
, монады обладают своим классом типов: Monad
! (Ух ты, кто бы мог подумать?)
class Monad m where
return :: a –> m a
(>>=) :: m a –> (a –> m b) –> m b
(>>) :: m a –> m b –> m b
x >> y = x >>= \_ –> y
fail :: String –> m a
fail msg = error msg
В первой строке говорится class Monad m where
. Стойте, не говорил ли я, что монады являются просто расширенными аппликативными функторами? Не надлежит ли здесь быть ограничению класса наподобие class (Applicative m) => Monad m where
, чтобы тип должен был являться аппликативным функтором, прежде чем он может быть сделан монадой? Ладно, положим, надлежит, – но когда появился язык Haskell, людям не пришло в голову, что аппликативные функторы хорошо подходят для этого языка. Тем не менее будьте уверены: каждая монада является аппликативным функтором, даже если в объявлении класса Monad
этого не говорится.
Первой функцией, которая объявлена в классе типов Monad
, является return
. Она аналогична функции pure
, находящейся в классе типов Applicative
. Так что, хоть она и называется по-другому, вы уже фактически с ней знакомы. Функция return
имеет тип (Monad m) => a –> m a
. Она принимает значение и помещает его в минимальный контекст по умолчанию, который по-прежнему содержит это значение. Другими словами, она принимает нечто и оборачивает это в монаду. Мы уже использовали функцию return
при обработке действий ввода-вывода (см. главу 8). Там она понадобилась для получения значения и создания фальшивого действия ввода-вывода, которое ничего не делает, а только возвращает это значение. В случае с типом Maybe
она принимает значение и оборачивает его в конструктор Just
.
ПРИМЕЧАНИЕ. Функция return
ничем не похожа на оператор return
из других языков программирования, таких как C++ или Java. Она не завершает выполнение функции. Она просто принимает обычное значение и помещает его в контекст.
Следующей функцией является >>=
, или связывание. Она похожа на применение функции, но вместо того, чтобы получать обычное значение и передавать его обычной функции, она принимает монадическое значение (то есть значение с контекстом) и передаёт его функции, которая принимает обычное значение, но возвращает монадическое.
Затем у нас есть операция >>
. Мы пока не будем обращать на неё большого внимания, потому что она идёт в реализации по умолчанию, и её редко реализуют при создании экземпляров класса Monad
. Мы подробно рассмотрим её в разделе «Банан на канате».
Последним методом в классе типов Monad
является функция fail
. Мы никогда не используем её в нашем коде явно. Вместо этого её использует язык Haskell, чтобы сделать возможным неуспешное окончание вычислений в специальной синтаксической конструкции для монад, с которой вы встретитесь позже. Нам не нужно сейчас сильно беспокоиться по поводу этой функции.
Теперь, когда вы знаете, как выглядит класс типов Monad
, давайте посмотрим, каким образом для типа Maybe
реализован экземпляр этого класса!
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x