Выбор как обратная связь
Feb. 12th, 2012 08:03 pm> module Strict where > import Prelude hiding (id, (.)) > import Control.Arrow > import Control.CategoryНаписалась тут у меня некая функция. Поведение у неё следующее:
Казалось бы, ну и что? Такую функцию написать очень несложно, а проще всего — воспользоваться стандартной фунцией*Strict> lft (+1) (Left 5) Left 6 *Strict> lft (+1) (Right "aaa") Right "aaa"
left
.Однако, тип у неё — другой. Именно:
Именно так. Не*Strict> :t lft lft :: ArrowLoop a => a x y —> a (Either x z) (Either y z)
ArrowChoice
, а ArrowLoop
.Вот как эта функция устроена.
> lft :: ArrowLoop a => a x y -> a (Either x z) (Either y z) > lft a = loop $ f ^>> second (id &&& a) where > f (Left x, ~(x1, y1)) = (Left y1, x) > f (Right y, ~(x1, y1)) = (Right y, x1)Фокус вот в чём. Допустим на вход поступило некое
Left x
. Функция должна выбрать некие x1
и y1
. Затем x1
отбрасывается (то есть, неважно, какое x1
было выбрано), а остальное превращается в (Left y1, x)
. Затем первая часть (Left y1
) остаётся без изменений, а вторая... вторая превращается в (x, y)
, где y
— результат действия a
на x
. За счёт loop
именно эта пара станет исходными (x1, y1)
, что означает, в частности, что y1
— это то же самое, что y
, то есть результат действия a
на x
. Но именно Left y1
идёт на выход — получается то, что нам и нужно.Если же на вход поступает
Right y
, то, опять-таки, функция выбирает некие x1
и y1
, составляет пару (Right y, x1)
, затем первую часть оставляет без изменения, а вторую превращает в (x1, y2)
, где y2
— результат действия a
на x1
. В любом случае, на выход идёт то же самое Right y
— опять, что и требуется.К сожалению, у такой функции есть заметное отличие от
left
. Именно, функция left
позволяет выбирать — производить ли некоторый эффект, или не стоит. В данном случае эффект производится вне зависимости от того, что именно пришло на вход, причём если на вход поступил Right y
, то эффект будет произведён с неизвестно чем в качестве входного параметра. Лучше всего это видно на примере монады IO
:Тут пока всё в порядке.*Strict> runKleisli (lft (Kleisli putStrLn)) (Left "xxx") xxx Left ()
Да, попытка напечатать неопределённую строку приводит именно к такому результату.*Strict> runKleisli (lft (Kleisli putStrLn)) (Right 1) *** Exception: <<loop>>
Ещё очень забавно смотрится монада
[]
:— как и ожидалось; но*Strict> runKleisli (lft (Kleisli (replicate 5))) (Left 1) [Left 1,Left 1,Left 1,Left 1,Left 1]
Для сравнения, функция*Strict> runKleisli (lft (Kleisli (replicate 5))) (Right 2) [Right 2,Right 2,Right 2,Right 2,Right 2]
left
ведёт себя во втором случае совершенно иначе:В этом тесте произведённый эффект, по сути, не зависел от пришедшего параметра. Если же он будет зависеть от него,*Strict> runKleisli (left (Kleisli (replicate 5))) (Right 2) [Right 2]
как вот здесь, то вызов*Strict> runKleisli (lft (Kleisli (\n —> replicate n n))) (Left 3) [Left 3,Left 3,Left 3]
благополучно зависает.*Strict> runKleisli (lft (Kleisli (\n —> replicate n n))) (Right 4)
Вот.