Who is afraid of arrows, was Re: [Haskell-cafe] ANNOUNCE: Haskell
XML Toolbox Version 9.0.0
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)
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
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 ($).
>> 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?
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.
More information about the Haskell-Cafe