52700.fb2
Частично определённая функция имеет тип a -> Maybe b (рис. 6.2), если всё в порядке и значение было
вычислено, она вернёт (Just a), а в случае ошибки будет возвращено значение Nothing. Теперь мы можем
определить нашу функцию так:
pred :: Nat -> Maybe Nat
pred Zero
= Nothing
pred (Succ a)
= Just a
Для Zero предыдущий элемент не определён .
Составляем функции вручную
Значение функции pred завёрнуто в упаковку Maybe, и для того чтобы воспользоваться им нам придётся
разворачивать его каждый раз. Как будет выглядеть функция извлечения дважды предыдущего натурального
числа:
pred2 :: Nat -> Maybe Nat
pred2 x =
case pred x of
Just (Succ a) -> Just a
_
-> Nothing
Если мы захотим определить pred3, мы заменим pred в case-выражении на pred2. Вроде не такое уж и
длинное решение. Но всё же мы теряем все преимущества гибких функций, все преимущества бесточечного
стиля. Нам бы хотелось написать так:
pred2 :: Nat -> Maybe Nat
pred2 = pred >> pred
pred3 :: Nat -> Maybe Nat
pred3 = pred >> pred >> pred
Но компилятор этого не допустит.
Композиция
Для того чтобы понять как устроена композиция частично определённых функций изобразим её вычисле-
ние графически (рис. 6.3). Сверху изображены две частично определённых функции. Если функция f вернула
значение, то оно подставляется в следующую частично определённую функцию. Если же первая функция не
смогла вычислить результат и вернула Nothing, то считается что вся функция (f*> g) вернула Nothing.
Теперь давайте закодируем это определение в Haskell. При этом мы воспользуемся нашим классом
Kleisli. Аналогом функции id для частично определённых функций будет функция, которая просто за-
ворачивает значение в конструктор Just.
instance Kleisli Maybe where
idK
= Just
f *> g = \a -> case f a of
Nothing -> Nothing
Just b
-> g b
Смотрите, в case-выражении мы возвращаем Nothing, если функция f вернула Nothing, а если ей удалось
вычислить значение и она вернула (Just b) мы передаём это значение в следующую функцию, то есть
составляем выражение (g b).
Сохраним это определение в модуле Kleisli, а также определение для функции pred и загрузим модуль
в интерпретатор. Перед этим нам придётся добавить в список функций, которые мы не хотим импортировать
из Prelude функцию pred, она также уже определена в Prelude. Для определения нашей функции нам по-
требуется модуль Nat, который мы уже определили. Скопируем файл Nat. hs в ту же директорию, в которой