52700.fb2
-> m a -> m b
Функция *$ применяет специальную функцию к специальному значению, а функция +$ применяет обыч-
ную функцию к специальному значению. Определения выглядят также как и в случае обычной функции
применения, мы только меняем знаки для композиции:
f
$ a = (const a >> f) ()
f *$ a = (const a *> f) ()
f +$ a = (const a +> f) ()
Теперь мы можем не только нанизывать специальные функции друг на друга но и применять их к значе-
ниям. Добавим эти определения в модуль Kleisli и посмотрим как происходит применение в интерпрета-
торе. Одна тонкость заключается в том, что мы определяли применение в терминах класса Kleisli, поэтому
правильно было написать типы новых функций так:
infixr 0 +$, *$
(*$) :: Kleisli m => (a -> m b) -> m a -> m b
(+$) :: Kleisli m => (a -> b)
-> m a -> m b
Также мы определили приоритет выполнения операций.
Загрузим в интерпретатор:
*Kleisli> let three = Succ (Succ (Succ Zero))
*Kleisli> pred *$ pred *$ idK three
Just (Succ Zero)
*Kleisli> pred *$ pred *$ idK Zero
Nothing
Применение функций | 93
Обратите внимание на то как мы погружаем в мир специальных функций обычное значение с помощью
функции idK.
Вычислим третье поколение L-системы:
*Kleisli> next *$ next *$ next *$ idK ’a’
”abaab”
Мы можем использовать и другие функции на списках:
*Kleisli> next *$ tail $ next *$ reverse $ next *$ idK ’a’
”aba”
Применение функций многих переменных
С помощью функции +$ мы можем применять к специальным значениям обычные функции одного аргу-
мента. А что если нам захочется применить функцию двух аргументов?
Например если мы захотим сложить два частично определённых числа:
?? (+) (Just 2) (Just 2)
На месте ?? должна стоять функция типа:
?? :: (a -> b -> c) -> m a -> m b -> m c
Оказывается с помощью методов класса Kleisli мы можем определить такую функцию для любой обыч-
ной функции, а не только для функции двух аргументов. Мы будем называть такие функции словом liftN,
где N – число, указывающее на арность функции. Функция (liftN f) “поднимает” (от англ. lift) обычную
функцию f в мир специальных функций.
Функция lift1 у нас уже есть, это просто функция +$. Теперь давайте определим функцию lift2:
lift2 :: Kleisli m => (a -> b -> c) -> m a -> m b -> m c
lift2 f a b = ...
Поскольку функция двух аргументов на самом деле является функцией одного аргумента мы можем
применить первый аргумент с помощью функции lift1, посмотрим что у нас получится:
lift1