52700.fb2
f
:: (a -> b -> c)
a
:: m a
lift1 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 c
lift2 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 b
mf $$ ma = ( +$ ma) *$ mf
Вы можете убедиться в том, что это определение проходит проверку типов. Посмотрим как эта функция
работает в интерпретаторе на примере частично определённых и многозначных функций, для этого давайте
добавим в модуль Kleisli это определение и загрузим его в интерпретатор:
94 | Глава 6: Функторы и монады: теория
*Kleisli> :reload Kleisli
Ok, modules loaded: Kleisli, Nat.
*Kleisli> Just (+2) $$ Just 2
Just 4
*Kleisli> Nothing $$ Just 2
Nothing
*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 c
lift2 f a b = f’ $$ b
where f’ = lift1 f a
Мы можем записать это определение более кратко:
lift2 :: Kleisli m => (a -> b -> c) -> m a -> m b -> m c
lift2 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)