определение этой функции, мы назовём её $$:
( $$) :: Kleislim =>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) $$ Just2
Just4
*Kleisli> Nothing $$ Just2
Nothing
*Kleisli>[( +1), ( +2), ( +3)] $$[10,20,30]
[11,21,31,12,22,32,13,23,33]
*Kleisli>[( +1), ( +2), ( +3)] $$ []
[]
Обратите внимание на то, что в случае списков были составлены все возможные комбинации применений.
Мы применили первую функцию из списка ко всем аргументам, потом вторую функцию, третью и объединили
все результаты в список.
Теперь мы можем закончить наше определение для lift2:
lift2 :: Kleislim =>(a ->b ->c) ->m a ->m b ->m c
lift2 f a b =f’ $$b
wheref’ =lift1 f a
Мы можем записать это определение более кратко:
lift2 :: Kleislim =>(a ->b ->c) ->m a ->m b ->m c
lift2 f a b =lift1 f a $$b
Теперь давайте добавим это определение в модуль Kleisliи посмотрим в интерпретаторе как работает
эта функция:
*Kleisli> :reload
[2 of2] Compiling Kleisli
( Kleisli.hs, interpreted )
Ok, modules loaded : Kleisli, Nat.
*Kleisli>lift2 ( +) ( Just2) ( Just2)
Just4
*Kleisli>lift2 ( +) ( Just2) Nothing
Nothing
Как на счёт функций трёх и более аргументов? У нас уже есть функции lift1 и lift2 определим функцию
lift3:
lift3 :: Kleislim =>(a ->b ->c ->d) ->m a ->m b ->m c ->m d lift3 f a b c = ...
Первые два аргумента мы можем применить с помощью функции lift2. Посмотрим на тип получивше-
гося выражения:
lift2
:: Kleislim =>(a’ ->b’ ->c’) ->m a’ ->m b’ ->m c’
f
::a ->b ->c ->d
lift2 f a b ::m (c ->d)
-- a’ == a, b’ == b, c’ == c -> d
У нас опять появился тип m (c ->d) и к нему нам нужно применить значение m c, чтобы получить m d.
Этим как раз и занимается функция $$. Итак итоговое определение примет вид:
lift3 :: Kleislim =>(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 :: Kleislim =>[m a] ->m [a]
sequence =foldr (lift2 ( :)) (idK [])
Мы “спрячем” из Preludeодноимённую функцию sequence. Посмотрим на примеры:
*Kleisli>sequence [ Just1, Just2, Just3]
Just[1,2,3]
*Kleisli>sequence [ Just1, Nothing, Just3]
Nothing
Во второй команде вся функция вернула Nothingпотому что при объединении списка встретилось зна-
чение Nothing, это равносильно тому, что мы объединяем в один список, значения полученные из функций,
которые могут не вычислить результат. Поскольку значение одного из элементов не определено, весь список
не определён.
Посмотрим как работает эта функция на списках:
*Kleisli>sequence [[1,2,3], [11,22]]
[[1,11],[1,22],[2,11],[2,22],[3,11],[3,22]]
Она составляет список всех комбинаций элементов из всех подсписков.
С помощью этой функции мы можем определить функцию mapK. Эта функция является аналогом обычной
функции map, но она применяет специальную функцию к списку значений.
Читать дальше