Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Arrows are exactly equivalent to applicative functors?

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 
like image 473
Cactus Avatar asked Jul 10 '14 04:07

Cactus


People also ask

Could you comfortably explain the difference between a Monad and an applicative functor?

Functors apply a function to a wrapped value: Applicatives apply a wrapped function to a wrapped value: Monads apply a function that returns a wrapped value to a wrapped value. Monads have a function >>= (pronounced "bind") to do this.

Is maybe an applicative functor?

Maybe is also an applicative functor, but more exist. The next article will give you another example. Next: Applicative validation.

Are all monads functors?

As I understand, every monad is a functor but not every functor is a monad. A functor takes a pure function (and a functorial value) whereas a monad takes a Kleisli arrow, i.e. a function that returns a monad (and a monadic value).

Is functor a Typeclass?

The Functor typeclass represents the mathematical functor: a mapping between categories in the context of category theory. In practice a functor represents a type that can be mapped over.


2 Answers

Every applicative yields an arrow and every arrow yields an applicative, but they are not equivalent. If you have an arrow arr and a morphism arr a b it does not follow that you can generate a morphism arr o (a \to b) that replicates its functionality. Thus if you round trip through applicative you lose some features.

Applicatives are monoidal functors. Arrows are profunctors that are also categories, or equivalently, monoids in the category of profunctors. There is no natural connection between these two notions. If you will excuse my flippancy: In Hask it turns out that the functor part of the pro-functor in an arrow is a monoidal functor, but that construction necessarily forgets the "pro" part.

When you go from arrows to applicatives you are ignoring the part of an arrow that takes input and only using the part that deals with output. Many interesting arrows use the input part in one way or another and so by turning them into applicatives you are giving up useful stuff.

That said, in practice I find applicative the nicer abstraction to work with and one that almost always does what I want. In theory arrows are more powerfull, but I don't find my self using them in practice.

like image 77
Philip JF Avatar answered Oct 04 '22 06:10

Philip JF


Let's compare the IO applicative functor with the Kleisli arrows of the IO monad.

You can have an arrow that prints a value read by a previous arrow:

runKleisli ((Kleisli $ \() -> getLine) >>> Kleisli putStrLn) () 

But you can't do that with applicative functors. With applicative functors, all the effects take place before applying the function-in-the-functor to the arguments-in-the-functor. The function-in-the-functor can't use the value inside an argument-in-the-functor to "modulate" its own effect, so to speak.

like image 35
danidiaz Avatar answered Oct 04 '22 06:10

danidiaz