Его экземпляр класса >Monad
похож на экземпляр для типа >Maybe
и может быть обнаружен в модуле >Control.Monad.Error
[15]:
>instance (Error e) => Monad (Either e) where
> return x = Right x
> Right x >>= f = f x
> Left err >>= f = Left err
> fail msg = Left (strMsg msg)
Функция >return
, как и всегда, принимает значение и помещает его в минимальный контекст по умолчанию. Она оборачивает наше значение в конструктор >Right
, потому что мы используем его для представления успешных вычислений, где присутствует результат. Это очень похоже на определение метода >return
для типа >Maybe
.
Оператор >>>=
проверяет два возможных случая: >Left
и >Right
. В случае >Right
к значению внутри него применяется функция >f
, подобно случаю >Just
, где к его содержимому просто применяется функция. В случае ошибки сохраняется значение >Left
вместе с его содержимым, которое описывает неудачу.
Экземпляр класса >Monad
для типа >Either e
имеет дополнительное требование. Тип значения, содержащегося в >Left
, – тот, что указан параметром типа >e
, – должен быть экземпляром класса >Error
. Класс >Error
предназначен для типов, значения которых могут действовать как сообщения об ошибках. Он определяет функцию >strMsg
, которая принимает ошибку в виде строки и возвращает такое значение. Хороший пример экземпляра >Error
– тип >String
! В случае со >String
функция >strMsg
просто возвращает строку, которую она получила:
>ghci> :t strMsg
>strMsg :: (Error a) => String –> a
>ghci> strMsg "Бум!" :: String
>"Бум!"
Но поскольку при использовании типа >Either
для описания ошибки мы обычно задействуем тип >String
, нам не нужно об этом сильно беспокоиться. Когда сопоставление с образцом терпит неудачу в нотации >do
, то для оповещения об этой неудаче используется значение >Left
.
Вот несколько практических примеров:
>ghci> Left "Бум" >>= \x –>return (x+1)
>Left "Бум"
>ghci> Left "Бум " >>= \x –> Left "нет пути!"
>Left "Бум "
>ghci> Right 100 >>= \x –> Left "нет пути!"
>Left "нет пути!"
Когда мы используем операцию >>>=
, чтобы передать функции значение >Left
, функция игнорируется и возвращается идентичное значение >Left
. Когда мы передаём функции значение >Right
, функция применяется к тому, что находится внутри, но в данном случае эта функция всё равно произвела значение >Left
!
Использование монады >Error
очень похоже на использование монады >Maybe
.
ПРИМЕЧАНИЕ. В предыдущей главе мы использовали монадические аспекты типа >Maybe
для симуляции приземления птиц на балансировочный шест канатоходца. В качестве упражнения вы можете переписать код с использованием монады >Error
, чтобы, когда канатоходец поскальзывался и падал, вы запоминали, сколько птиц было на каждой стороне шеста в момент падения.