Точка зрения A немного странная - вообще говоря, абстракция не будет одновременно более мощной и более общей, чем какая-либо другая абстракция; эти двое расходятся. Иметь «больше возможностей» означает знать больше о структуре того, с чем вы работаете, а это означает больше ограничений. С одной стороны, вы точно знаете, с каким типом работаете. Это очень мощно; вы можете применить к нему любую допустимую функцию. С другой стороны, это также не является общим: код, написанный с таким предположением, применим только к этому типу! С другой стороны, вы можете ничего не знать о своем типе (например, о наличии переменной типа a
). Это очень общий подход, применимый к каждому типу, но он также не очень эффективен, поскольку у вас вообще недостаточно информации, чтобы что-то делать!
Пример, более основанный на реальном коде, - это разница между Functor
и Applicative
. Здесь Functor
является более общим - строго больше типов Functor
, чем Applicative
, поскольку каждый Applicative
также является Functor
, но не наоборот. Однако, поскольку Applicative
несет больше структуры, он строго более мощный. С Functor
вы можете отображать только функции с одним аргументом над вашим типом; с Applicative
вы можете отображать функции с любым количеством аргументов. Опять же: один более общий, другой более мощный.
Так что это? Стрелы более мощные или более общие, чем монады? Это более сложный вопрос, чем сравнение функторов, аппликативных функторов и монад, потому что стрелки - совсем другое дело. У них даже есть другой вид: у монад и других есть вид * -> *
, а у стрелок - вид * -> * -> *
. К счастью, оказывается, что мы можем идентифицировать стрелки с аппликативными функторами / монадами, так что мы действительно можем дать осмысленный ответ на этот вопрос: стрелки более общие, чем монады, и, следовательно, менее мощные. Для любой монады мы можем построить из нее стрелу, но мы не можем построить монаду для каждой стрелки.
Основная идея заключается в следующем:
instance Monad m => Category (a -> m b) where
id = return
(f . g) x = g x >>= f
instance Monad m => Arrow (a -> m b) where
arr f = return . f
first f (x, y) = f x >>= \ x' -> return (x', y)
Однако, поскольку у нас есть экземпляр стрелки для a -> b
, мы должны обернуть a -> m b
в newtype
в реальном коде. Этот newtype
называется Klesli
(из-за категорий Klesli).
Однако мы не можем пойти другим путем - не существует конструкции, позволяющей получить Monad
из любого Arrow
. Это происходит потому, что вычисление Arrow
не может изменить свою структуру на основе значений, проходящих через него, в то время как монады могут. Единственный способ обойти это - добавить мощности вашей абстракции стрелки с помощью некоторой дополнительной примитивной функции; это именно то, что делает ArrowApply
.
Оператор >>>
для стрелок является обобщением .
для функций и поэтому имеет те же общие ограничения. >>=
, с другой стороны, больше похож на обобщение функции application. Обратите внимание на типы: для >>>
обе стороны - стрелки; для >>=
первый аргумент - это значение (m a
), а второй - функция. Более того, результат >>>
- это еще одна стрелка, где результат >>=
- это значение. Поскольку стрелки имеют только >>>
, но не имеют понятия, эквивалентного >>=
, вы не можете «применять» их к аргументам в целом - вы можете создавать только конвейеры со стрелками. Фактическая функция применения / запуска должна быть специфичной для любой данной стрелки. С другой стороны, монады определяются в терминах >>=
и поэтому по умолчанию имеют некоторое понятие приложения.
ArrowApply
просто дополняет стрелки на app
, что является общим понятием приложения. Представим себе нормальное приложение функции:
apply :: (b -> c) -> b -> c
apply f x = f x
мы можем распаковать это, чтобы получить:
apply :: ((b -> c), b) -> c
Стрелки обобщают функции, в основном заменяя ->
переменной (a
). Сделаем это для apply
, заменив оба вхождения ->
на инфикс a
:
apply :: (b `a` c, b) `a` c
Мы все еще можем видеть ту же структуру, что и первая версия apply
, только без спешки и с `a`
вместо ->
. Теперь, если мы просто избавимся от обратных кавычек и сделаем префикс a
, мы получим подпись для app
:
app :: a (a b c, b) c
Итак, мы видим, как ArrowApply
просто добавляет к стрелкам понятие приложения. Это аналог >>=
, который является понятием приложения для монад (или, в частности, функций формы a -> m b
). Этой дополнительной структуры достаточно, чтобы построить монаду из стрелки, поэтому ArrowApply
изоморфен Monad
.
Зачем нам вообще их использовать? Честно говоря, не думаю, что мы бы стали. Стрелки сильно переоценены, поэтому придерживайтесь монад и аппликативных функторов.
16.07.2013
ArrowApply
16.07.2013m
как оarrowFromTo
. Первый пример для Arrows - это гдеm
равно(->)
. Если вы перечитаете мой ответ на вопрос 3, вы увидите, что я заменил(~>)
наm
, поэтому я могу использовать его инфиксным. (Обратите внимание на разницу между->
и~>
.->
- это приложение-функция, а~>
- какое-тоarrowFromTo
.) Помните, что стрелки делают разные вещи;m b c
используется очень похоже наb -> m c
из Monads, поэтому я вызываюm
~>
и пишуb ~> c
вместо(~>) b c
. (Итак,>>>
в Arrows работает очень похоже на>=>
из Control.Monad.) 17.07.2013>>>
позволяет выполнять секвенирование без возможности изменения результата предыдущего аргумента, как>=>
для монад.>=>
имеет подпись типаMonad m => (b -> m c) -> (c -> m d) -> (b -> m d)
, которая может быть написанаMonad m => ((->) b (m c)) -> ((->) c (m d)) -> ((->) b (m d)
. Сравните это сArrow (~>) => ((~>) b c) -> ((~>) c d) -> ((~>) b d)
или, что эквивалентно,Arrow (~>) => (b ~> c) -> (c ~> d) -> (b ~> d)
17.07.2013Monad m => b -> m c
похоже наArrow (~>) => b ~> c
, обозначенный стрелками, или, что эквивалентно,Monad m => (->) b (m c)
похоже наArrow (~>) => (~>) b c
, что то же самое, что иArrow a => a b c
. В Monads вы позволяете типу возвращаемого значенияm c
изменяться сложным образом, в Arrows вы разрешаете сложной сигнатуре типа всей функционально-подобной операции. 17.07.2013(>>=) = flip use
, ноuse :: Applicative f => f (a -> f b) -> f a -> f b
- мне кажется, что первый аргументuse
имеет дополнительныйf
по сравнению со вторым аргументом(>>=)
. 01.08.2013pure
. Исправлено сейчас. Та. 03.08.2013ArrowApply
иMonad
должны быть эквивалентными? Очевидно, что по одному из них мы можем определить экземпляр другого, но являются ли карты обратными друг другу? ВозьмитеArrowApply (~>)
, определите егоMonad m
, почемуa -> m b
должен быть эквивалентенa ~> b
? Возьмем, к примеру,a ~> b := a -> (b, a ~> b)
, который является потоковым процессором с отслеживанием состояния, затемm b = () -> (b, m b)
, так чтоa -> m b = a -> (b, m b) = a -> (b, () ~> b)
, что совсем не эквивалентноa ~> b = a -> (b, a ~> b)
. 12.08.2015