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

Gregory Crosswhite gcross at phys.washington.edu
Mon Oct 11 14:48:40 EDT 2010


Thank you for your reply.

On 10/11/10 6:20 AM, Uwe Schmidt wrote:
> I thing, this is not a question of functionality, it's a question of style.
> Of course everything in hxt could have been done with monads,
> but does this also mean: Everything must have been done with monads?
No, but there is no point in using a formalism that adds complexity 
without adding functionality.  Arrows are more awkward to use than 
monads because they were intentionally designed to be less powerful than 
monads in order to cover situations in which one could not use a monad.  
When your problem is solved by a monad there is no point in using arrows 
since an arrow require you to jump through extra hoops to accomplish the 
same goal.
> Arrows are a generalisation of functions, so if you know all about working
> with functions you know (almost) all about working with arrows.
> When generalizing a pure algorithm, something like "f3 . f2 . f1",
> or in Unix pipe style "f1>>>  f2>>>  f3", you don't have to rewrite
> the code, you just generalize function composition.

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.
> When constructing code, it is of course sometimes simpler to start with a
> point wise version and then refactor and rewrite it into a more compact point
> free version. The problem with arrows seems, that the arrow style forces to
> start with the point free style. And that may be the main hurdle in mind.
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.  Second, they place restrictions on how 
you define the input arguments of the arrow because you can't feed the 
output of one arrow into to input of the next unless said input is 
captured in the arrows type.

To be more concrete about my second point, suppose you have some monadic 

     f :: a -> b -> m c

How would you structure this same action as an arrow?  One thing that 
you could do is take one of the arguments and turn it into the input of 
the arrow:

      f' :: a -> Arrow b c

But now you can't feed the output of an arrow into the first argument.  
Alternatively, you could pack both arguments into the input of the arrow:

     f'' :: Arrow (a,b) c

Great, but now you have made it more awkward to use f'' because you have 
to always pack the arguments into a tuple, so that for example if one of 
the arguments is a constant then you can no longer easily use currying.  
The advantage of f over both alternatives is that you don't have to 
waste any of your time fitting multiple input arguments into the input 
type of an arrow.

In fact, if the first argument to f'' is usually a constant, then f'' is 
arguably more awkward to use in a point-free setting than f, because 
instead of being able to write

     a >=> f 42 >=> b

You have to write something like

     a >>> (const 42 &&& id) >>> f'' >>> b

Of course, if the first argument were *always* a constant then you could 
use f', but then you lose the ability to ever feed in the output of an 
arrow into the first argument.  So in short, arrows force you to make 
choices and trade-offs that never come up when using monads.

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.


More information about the Haskell-Cafe mailing list