Who is afraid of arrows, was Re: [Haskell-cafe] ANNOUNCE: Haskell XML Toolbox Version 9.0.0

Gregory Crosswhite gcross at phys.washington.edu
Tue Oct 12 16:02:06 EDT 2010


  On 10/12/10 5:56 AM, Uwe Schmidt wrote:
> Hi Gregory,
>
> As I understood, John Hughes invented the arrows as a generalisation
> of monads, you say it's a less powerful concept. I'm a bit puzzled with
> that. Could you explain these different views.
>

Consider the following example:

     f :: Int -> m a
     f i = monads !! (i *5 `mod` length monads)
         where
             monads = [...] :: [m a]

In this case, we see that the action chosen by f is a non-trivial 
function of the input argument.  In general, there is no way that we can 
represent this equivalently as an arrow.  The reason why is because, 
unlike monads, in general arrows do not give you arbitrary freedom to 
choose the action performed by the arrow.  It is true that one can get 
this freedom back for arrows which are instances of ArrowApply, but that 
that point what you really have is a monad with different notation.  
Hughes himself said that when your arrow is an instance of ArrowApply, 
you are better off just sticking with monads.

The reason for the existence of arrows is because allowing the user to 
arbitrarily choose actions restricts the power that the creator of a 
library has to do stuff behind the scenes with these actions.  To see 
why, suppose we are chaining together two arrows like so:

     f :: Arrow a b
     g :: Arrow b c
     h :: Arrow a c
     h = f >>> g

When defining the composition operator >>>, the library author has the 
power to "open up" both of the actions f and g, which gives him lots of 
power in defining what h should be.  By contrast, suppose we are 
chaining together two monads like this:

     f :: Monad b
     g :: b -> Monad c
     h :: Monad c
     h = f >>= g

When defining the composition operator >>=, the library author can look 
inside f, but he has no way of knowing which action will be chosen by g, 
so he cannot use this information to decide what h should be.

The motivation Hughes gave for arrows was parsers that have a "static" 
part in them;  he pointed out that using monads we have no way to access 
and combine these static parts, so we needed to come up with a formalism 
that let us have a nice combinator framework like monads but which 
allows us to "break open" components so that we can combine them in 
interesting ways.

So in short, arrows (relative to monads) take away some of the power of 
the user to choose actions as a function of the input in order to give 
library authors more power to define how actions are combined.  In this 
sense arrows are "less powerful" from a user perspective than monads, 
except in the case where an arrow is an instance of ArrowApply in which 
case they are

>> ...
>> Yes, but the>=>  operator lets you do the same thing with monads, and in
>> fact I use it all the time to do point-free programming with monads, so
>> this isn't at all an advantage that arrows have over monads.
> yes, I agree. What about the other combinators (&&&, ***,...)?
>

A couple of points in response.  First, though it is less convenient, 
you can use those combinators if you wrap your arrow inside the Kleisli 
newtype wrapper, which is something I have occasionally done.  Second, 
while I see your point that there are some combinators that arrows have 
that are not defined for monads, there are not that many of them, and it 
would be trivial to write special instances of them for monads;  it 
seems to me that the price of making versions of these operators for 
monads is less than the price of having to re-implement all of the 
existing monad combinators and libraries using arrow notation just to 
not have to redefined monad versions of &&&, etc.

>> ...
>> No, that is not at all the problem with arrows.  The problem with arrows
>> is that they are more restrictive than monads in two respects.  First,
>> unlike monads, in general they do not let you perform an arbitrary
>> action in response to an input. ...
> It's rather easy to define some choice combinators. Or am I
> missing the point?
>

No, in general arrows do not allow you to define choice combinators.  
Having said that, you *can* give the user some power to choose between 
fixed choices action in exchange for possibly losing power from your 
perspective a library author.  If your arrow is an instance of 
ArrowChoice, then you give your user the ability to choose between a 
fixed set of predefined actions.  However, the user is not able to 
compute an arbitrary action in response to an input.  To get this power, 
you need to make your arrow an instance of ArrowApply, in which case you 
really aren't getting any benefit from the perspective of either a user 
*or* a library author over using a monad.

> yes, this a common pattern, a function f' with an extra argument of type a,
> and sometimes you want to call f' with some value, e.g. f' 42,
> sometimes the extra argument must be computed from the arrow input, let's say
> with an arrow g.
>
> For this case in hxt there is a combinator ($<) with signature
>
> ($<) :: (c ->  a b d) ->  a b c ->  a b d
>
> With that combinator you can write
>
> f' $<  g
>
> The combinator does the following: The input of the whole arrow
> is fed into g, g computes some result and this result together with the
> input is used for evaluating f'.  The ($<) is something similar to ($).
>

Fair enough.

>> ...
>> In conclusion, while I greatly appreciate you taking the time to explain
>> your reasoning, it still looks to me like there is nothing you have
>> gained by using arrows except adding extra unnecessary complexity in
>> your library.
> So, your advice is to throw away the whole arrow stuff in hxt-10 and
> redefined (or rename) the combinators on the basis of monads?
>
> Cheers,
>
>    Uwe
>

Yes, of course it is!!!  Of course, given how much work you put into 
hxt, you would most likely be a fool if you actually took my advice...  ;-)

So, no, despite the way I am probably coming across I am not actually 
trying to convince you to rewrite your library from scratch to use 
monads.  My actual goals are twofold:  First (driven by genuine 
curiosity) to see if there is something that I missed that made arrows 
be the natural choice for you to use in your library.  Second, to try 
and convince people in the future who are considering basing their 
libraries on arrows that they should only do this if monads do not give 
them enough power as library authors.

Cheers,
Greg


More information about the Haskell-Cafe mailing list