Ответ 1
Ответ заключается в следующем (все это из Документов Control.Arrow)
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
instance Monad ArrowApply a => Monad (ArrowMonad a)
Новый тип ArrowMonad
- это средство, с помощью которого мы определяем экземпляр Monad
для стрелок ArrowApply
. Мы могли бы использовать
instance Monad ArrowApply a => Monad (a ())
но это могло бы вызвать проблемы с ограниченным типом класса Haskell (он будет работать с расширением UndecideableInstances
, я понял).
Вы можете придумать экземпляр Monad
для ArrowApply
стрелок как перевод монадических операций в эквивалентные операции со стрелками, как показывает источник:
instance ArrowApply a => Monad (ArrowMonad a) where
return x = ArrowMonad (arr (\_ -> x))
ArrowMonad m >>= f = ArrowMonad (m >>>
arr (\x -> let ArrowMonad h = f x in (h, ())) >>>
app)
Знаем, что мы знаем, что ArrowApply
так же эффективен, как Monad
, так как мы можем реализовать все операции Monad
в нем. Удивительно, но верно и обратное. Это дается Kleisli
newtype, как отмечал @hammar. Обратите внимание:
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
id = Kleisli return
(Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)
instance Monad m => Arrow (Kleisli m) where
arr f = Kleisli (return . f)
first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))
instance Monad m => ArrowApply (Kleisli m) where
app = Kleisli (\(Kleisli f, x) -> f x)
instance Monad m => ArrowChoice (Kleisli m) where
left f = f +++ arr id
right f = arr id +++ f
f +++ g = (f >>> arr Left) ||| (g >>> arr Right)
Kleisli f ||| Kleisli g = Kleisli (either f g)
В предыдущем приведена реализация для всех обычных операций со стрелками с использованием операций монады. (***)
не упоминается, поскольку имеет реализацию по умолчанию usin first
и second
:
f *** g = first f >>> second g
Итак, теперь мы знаем, как реализовать операции стрелки (Arrow
, ArrowChoice
, ArrowApply
) с использованием операций Monad.
Чтобы ответить на вопрос о том, почему мы имеем Monad
и Arrow
, если они оказываются эквивалентными:
Менее мощные стрелки полезны, когда нам не нужна полная сила монады, так же как аппликативные функторы могут быть полезны. И хотя ArrowApply
и Monad
эквивалентны, Arrow
или ArrowChoice
без app
- это то, что не представляется в иерархии Monad
. И наоборот, Applicative
не представляется в иерархии стрелок.
Это связано с тем, что ap
входит в первую очередь в иерархию монады и "последний" в иерархии стрелок.
Основное семантическое различие между мирами монады и стрелки состоит в том, что стрелки захватывают преобразование (arr b c
означает, что мы создаем c
из b
), в то время как монады захватывают операцию (monad a
создает a a
). Эта разница хорошо отражена в новинках Kleisli
и ArrowMonad
:
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
newtype ArrowApply a => ArrowMonad a b = ArrowMonad (a () b)
В Kleisli
мы должны добавить тип источника a
, а в ArrowMonad
мы установим его в ()
.
Я надеюсь, что это удовлетворит вас!