следовательного применения функций. Разные значки отражают разные типы функций аргументов.
Обобщённая формулировка категории Клейсли
Отметим, что мы могли бы сформулировать класс Kleisliи в более общем виде с помощью класса
Category:
class Kleislim where
idK
:: Categorycat =>cat a (m a)
( *>) :: Categorycat =>cat a (m b) ->cat b (m c) ->cat a (m c)
( +>) ::( Categorycat, Kleislim)
=>cat a (m b) ->cat b c ->cat a (m c)
f +>g =f *>(g >>idK)
Мы заменили функциональный тип на его обобщение. Для наглядности мы будем пользоваться специ-
альной формулировкой со стрелочным типом.
Для этого мы определим модуль Kleisli.hs
module Kleisli where
import Prelude hiding(id, ( >>))
class Categorycat where
id
::cat a a
( >>) ::cat a b ->cat b c ->cat a c
class Kleislim where
idK
::a ->m a
( *>) ::(a ->m b) ->(b ->m c) ->(a ->m c)
( +>) :: Kleislim =>(a ->m b) ->(b ->c) ->(a ->m c)
f +>g =f *>(g >>idK)
-- Экземпляр для функций
instance Category( ->) where
id
=\x ->x
f >>g
=\x ->g (f x)
Мы не будем импортировать функцию id, а определим её в классе Category. Также в Preludeуже опре-
делена функция ( >>) мы спрячем её с помощью специальной директивы hiding для того, чтобы она нам не
мешалась. Далее мы будем дополнять этот модуль экземплярами класса Kleisliи примерами.
6.2 Примеры специальных функций
Частично определённые функции
Частично определённые функции – это такие функции, которые определены не для всех значений аргу-
ментов. Примером такой функции может быть функция поиска предыдущего числа для натуральных чисел.
Поскольку числа натуральные, то для нуля такого числа нет. Для описания этого поведения мы можем вос-
пользоваться специальным типом Maybe. Посмотрим на его определение:
data Maybea = Nothing | Justa
deriving( Show, Eq, Ord)
88 | Глава 6: Функторы и монады: теория
a
f
b
Nothing
Рис. 6.2: Частично определённая функция
Частично определённая функция имеет тип a -> Maybeb (рис. 6.2), если всё в порядке и значение было
вычислено, она вернёт ( Justa), а в случае ошибки будет возвращено значение Nothing. Теперь мы можем
определить нашу функцию так:
pred :: Nat -> Maybe Nat
pred Zero
= Nothing
pred ( Succa)
= Justa
Для Zeroпредыдущий элемент не определён .
Составляем функции вручную
Значение функции pred завёрнуто в упаковку Maybe, и для того чтобы воспользоваться им нам придётся
разворачивать его каждый раз. Как будет выглядеть функция извлечения дважды предыдущего натурального
числа:
pred2 :: Nat -> Maybe Nat
pred2 x =
casepred x of
Just( Succa) -> Justa
_
-> 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 для частично определённых функций будет функция, которая просто за-
Читать дальше