*Kleisli>
pred *$ pred *$ idK ZeroNothing
Применение функций | 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 clift2 f a b = ...
Поскольку функция двух аргументов на самом деле является функцией одного аргумента мы можем
применить первый аргумент с помощью функции lift1, посмотрим что у нас получится:
lift1
::
(a’ -> b’) -> m’ a’ -> m’ b’f
::
(a -> b -> c)a
::
m alift1 f a
::
m (b -> c)-- m’ == m, a’ == a, b’ == b -> c
Теперь в нашем определении для lift2 появится новое слагаемое g:
lift2 :: Kleisli
m => (a -> b -> c) -> m a -> m b -> m clift2 f a b = ...
where
g = lift1 f aОдин аргумент мы применили, осталось применить второй. Нам нужно составить выражение (g b), но
для этого нам нужна функция типа:
m (b ->
c) -> m b -> m cЭта функция применяет к специальному значению функцию, которая завёрнута в тип m. Посмотрим на
определение этой функции, мы назовём её $$
:($$
) :: Kleisli m => m (a -> b) -> m a -> m bmf $$
ma = ( +$ ma) *$ mfВы можете убедиться в том, что это определение проходит проверку типов. Посмотрим как эта функция
работает в интерпретаторе на примере частично определённых и многозначных функций, для этого давайте
добавим в модуль Kleisli
это определение и загрузим его в интерпретатор:94 | Глава 6: Функторы и монады: теория
*Kleisli> :
reload KleisliOk
, modules loaded: Kleisli, Nat.*Kleisli> Just
(+2) $$ Just 2Just
4*Kleisli> Nothing $$ Just
2Nothing
*Kleisli>
[(+1), (+2), (+3)] $$ [10,20,30][11,21,31,12,22,32,13,23,33]
*Kleisli>
[(+1), (+2), (+3)] $$ [][]
Обратите внимание на то, что в случае списков были составлены все возможные комбинации применений.
Мы применили первую функцию из списка ко всем аргументам, потом вторую функцию, третью и объединили
все результаты в список.
Теперь мы можем закончить наше определение для lift2:
lift2 :: Kleisli
m => (a -> b -> c) -> m a -> m b -> m clift2 f a b =
f’ $$ bwhere
f’ = lift1 f aМы можем записать это определение более кратко:
lift2 :: Kleisli
m => (a -> b -> c) -> m a -> m b -> m clift2 f a b =
lift1 f a $$ bТеперь давайте добавим это определение в модуль Kleisli
и посмотрим в интерпретаторе как работаетэта функция:
*Kleisli> :
reload[2 of
2] Compiling Kleisli( Kleisli.
hs, interpreted )Ok
, modules loaded: Kleisli, Nat.*Kleisli>
lift2 (+) (Just 2) (Just 2)Just
4*Kleisli>
lift2 (+) (Just 2) NothingNothing
Как на счёт функций трёх и более аргументов? У нас уже есть функции lift1 и lift2 определим функцию
lift3:
lift3 :: Kleisli
m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d lift3 f a b c = ...Первые два аргумента мы можем применить с помощью функции lift2. Посмотрим на тип получивше-
гося выражения:
lift2
:: Kleisli
m => (a’ -> b’ -> c’) -> m a’ -> m b’ -> m c’f
::
a -> b -> c -> dlift2 f a b ::
m (c -> d)-- a’ == a, b’ == b, c’ == c -> d
У нас опять появился тип m (c ->
d) и к нему нам нужно применить значение m c, чтобы получить m d.Этим как раз и занимается функция $$
. Итак итоговое определение примет вид:lift3 :: Kleisli
m => (a -> b -> c -> d) -> m a -> m b -> m c -> m d lift3 f a b c = lift2 f a b $$ cТак мы можем определить любую функцию liftN через функции liftN-
1 и $$.Несколько полезных функций
Теперь мы умеем применять к специальным значениям произвольные обычные функции. Определим ещё
несколько полезных функций. Первая функция принимает список специальных значений и собирает их в
специальный список:
Применение функций | 95
import Prelude hiding
(id, (>> ), pred, sequence)sequence :: Kleisli
m => [m a] -> m [a]sequence =
foldr (lift2 (:)) (idK [])