According to the famous paper Idioms are oblivious, arrows are meticulous, monads are promiscuous, the expressive power of arrows (without any additional typeclasses) should be somewhere strictly between applicative functors and monads: monads are equivalent to ArrowApply
, and Applicative
should be equivalent to something the paper calls "static arrows". However, it is not clear to me what restriction this "static"-ness means.
Playing around with the three typeclasses in question, I was able to build up an equivalence between applicative functors and arrows, which I present below in the context of the well-known equivalence between Monad
and ArrowApply
. Is this construction correct? (I've proven most of the arrow laws before getting bored of it). Doesn't that mean that Arrow
and Applicative
are exactly the same?
{-# LANGUAGE TupleSections, NoImplicitPrelude #-}
import Prelude (($), const, uncurry)
-- In the red corner, we have arrows, from the land of * -> * -> *
import Control.Category
import Control.Arrow hiding (Kleisli)
-- In the blue corner, we have applicative functors and monads,
-- the pride of * -> *
import Control.Applicative
import Control.Monad
-- Recall the well-known result that every monad yields an ArrowApply:
newtype Kleisli m a b = Kleisli{ runKleisli :: a -> m b}
instance (Monad m) => Category (Kleisli m) where
id = Kleisli return
Kleisli g . Kleisli f = Kleisli $ g <=< f
instance (Monad m) => Arrow (Kleisli m) where
arr = Kleisli . (return .)
first (Kleisli f) = Kleisli $ \(x, y) -> liftM (,y) (f x)
instance (Monad m) => ArrowApply (Kleisli m) where
app = Kleisli $ \(Kleisli f, x) -> f x
-- Every arrow arr can be turned into an applicative functor
-- for any choice of origin o
newtype Arrplicative arr o a = Arrplicative{ runArrplicative :: arr o a }
instance (Arrow arr) => Functor (Arrplicative arr o) where
fmap f = Arrplicative . (arr f .) . runArrplicative
instance (Arrow arr) => Applicative (Arrplicative arr o) where
pure = Arrplicative . arr . const
Arrplicative af <*> Arrplicative ax = Arrplicative $
arr (uncurry ($)) . (af &&& ax)
-- Arrplicatives over ArrowApply are monads, even
instance (ArrowApply arr) => Monad (Arrplicative arr o) where
return = pure
Arrplicative ax >>= f =
Arrplicative $ (ax >>> arr (runArrplicative . f)) &&& id >>> app
-- Every applicative functor f can be turned into an arrow??
newtype Applicarrow f a b = Applicarrow{ runApplicarrow :: f (a -> b) }
instance (Applicative f) => Category (Applicarrow f) where
id = Applicarrow $ pure id
Applicarrow g . Applicarrow f = Applicarrow $ (.) <$> g <*> f
instance (Applicative f) => Arrow (Applicarrow f) where
arr = Applicarrow . pure
first (Applicarrow f) = Applicarrow $ first <$> f
arr
equipped with an isomorphism betweenarr a b
andarr () (a -> b)
. – Tom Ellisclass (Arrow arr) => ArrowStatic arr where iso :: arr a b :<->: arr () (a -> b)
. Now, ifApplicative
is equivalent toArrowStatic
, shouldn't that mean I can turn anyApplicative
intoArrowStatic
, and thus,Arrow
? How does that imply thatArrow
is a more expressive interface? – CactusCategory
+Applicative
+ a few extra laws for the interaction between the two is the same asArrow
. cdsmith.wordpress.com/2011/08/13/…. The proof doesn't go as far as proving that anyApplicative
Category
is also anArrow
. We had a similar discussion aboutArrows
andApplicative
insprired by this tangentially related answer: stackoverflow.com/a/22875729/414413 – CirdecOverlappingInstances
, the only way for something that looks likep x
to be aFunctor
is forp
to be anArrow
! It breaks all sorts of everything, so Cactus thoughtfully didn't do it. – dfeuerOverlappingInstances
(blech!), this ends up being a good thing for equality constraints. Otherwise, I think it may be important for efficient resolution--not sure. And I think it's almost certainly important for making things sane without overlapping/incoherence. – dfeuer