этом определении. Зачем такое запутанное определение, вместо привычного (f a)? Оказывается точно таким
же способом мы можем определить применение в нашем мире специальных функций a ->m b.
Применение в этом мире происходит особенным образом. Необходимо помнить о том, что второй аргу-
мент функции применения, значение, которое мы подставляем в функцию, также было получено из какой-то
другой функции. Поэтому оно будет иметь такую же форму, что и значения справа от стрелки. В нашем
случае это m b.
Посмотрим на типы специальных функций применения:
( *$) ::(a ->m b) ->m a ->m b
( +$) ::(a ->b)
->m a ->m b
Функция *$применяет специальную функцию к специальному значению, а функция +$применяет обыч-
ную функцию к специальному значению. Определения выглядят также как и в случае обычной функции
применения, мы только меняем знаки для композиции:
f
$a =(const a >>f) ()
f *$a =(const a *>f) ()
f +$a =(const a +>f) ()
Теперь мы можем не только нанизывать специальные функции друг на друга но и применять их к значе-
ниям. Добавим эти определения в модуль Kleisliи посмотрим как происходит применение в интерпрета-
торе. Одна тонкость заключается в том, что мы определяли применение в терминах класса Kleisli, поэтому
правильно было написать типы новых функций так:
infixr0 +$, *$
( *$) :: Kleislim =>(a ->m b) ->m a ->m b
( +$) :: Kleislim =>(a ->b)
->m a ->m b
Также мы определили приоритет выполнения операций.
Загрузим в интерпретатор:
*Kleisli> letthree = 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”
Применение функций многих переменных
С помощью функции +$мы можем применять к специальным значениям обычные функции одного аргу-
мента. А что если нам захочется применить функцию двух аргументов?
Например если мы захотим сложить два частично определённых числа:
??( +) ( Just2) ( Just2)
На месте ??должна стоять функция типа:
?? ::(a ->b ->c) ->m a ->m b ->m c
Оказывается с помощью методов класса Kleisliмы можем определить такую функцию для любой обыч-
ной функции, а не только для функции двух аргументов. Мы будем называть такие функции словом liftN,
где N– число, указывающее на арность функции. Функция (liftN f) “поднимает” (от англ. lift) обычную
функцию f в мир специальных функций.
Функция lift1 у нас уже есть, это просто функция +$. Теперь давайте определим функцию lift2:
lift2 :: Kleislim =>(a ->b ->c) ->m a ->m b ->m c
lift2 f a b = ...
Поскольку функция двух аргументов на самом деле является функцией одного аргумента мы можем
применить первый аргумент с помощью функции lift1, посмотрим что у нас получится:
lift1
::(a’ ->b’) ->m’ a’ ->m’ b’
f
::(a ->b ->c)
a
::m a
lift1 f a
::m (b ->c)
-- m’ == m, a’ == a, b’ == b -> c
Теперь в нашем определении для lift2 появится новое слагаемое g:
lift2 :: Kleislim =>(a ->b ->c) ->m a ->m b ->m c
lift2 f a b = ...
whereg =lift1 f a
Один аргумент мы применили, осталось применить второй. Нам нужно составить выражение (g b), но
для этого нам нужна функция типа:
m (b ->c) ->m b ->m c
Эта функция применяет к специальному значению функцию, которая завёрнута в тип m. Посмотрим на
Читать дальше