There was a blog post that came out around that time in which it was claimed that arrows are easy to understand because they're just functions. There was a lot of backlash, and this comic is trying to point out that the reasoning behind it is backwards. Every function is an arrow, but not every arrow is a function. I believe the blog post in question is here:
That's about the limit of my comprehension of arrows. When you see a type (Arrow arr) => arr a b, you should read it as "an arrow from a to b", just like a function (a -> b) can be read "a function from a to b". But an arrow can encompass a monad, a function a -> m b is also an arrow, a Kleisli arrow with type Kleisli m a b. So this means you can use the arrow composition functions with normal functions as well as monadic functions, as well as arrows of other types which need not be functions at all.
Beyond that, all I know is that arrows provide composition functions that allow you to build a digraph of computations where inputs come in, get split off, passed through other arrows and recombined in interesting ways, and there is an arrow syntax which is so convoluted I've never been able to understand the relationship between it and the fundamental arrow combinators. I have never run into a situation where knowing anything about arrows was useful, other than because (&&&) is a neat combinator if you're want tuples. Practice doesn't seem to have caught up to the theory here, and things like applicative functors, which are simpler than monads in some sense, are a lot more useful than arrows, which are more complex. This blog post illustrates a case where arrows were handy, but he winds up using applicative functors in the end also:
It's a curious thing about functional programming that often what's simpler in theory (monoids, functors) turn out to be more useful in complex situations in the real world than what's more complex in theory.
One last note, I wrote a blog post about this in 2007 with FizzBuzz. Forgive me if it's horrible; I haven't given it much thought since, but here it is:
Thanks for the link, I hadn't seen the second one. I felt like I almost understood what they are for, and the constructors for the arrow datatype aren't very cryptic, but then it hits the code examples, which has stuff like
y <- readFileA "unicorns.txt" -< ()
and it's unlike anything I'm used to. I'd love to find a post that described the utility of arrows as well as his did and then maybe having a middle step where they are used without the arcane symbols, and then finally with the arcane symbols.
Your post was very clear (although your code blocks have a tiny font on my computer for some reason). The &&& and >>> combinators make sense, especially with your diagram... but in the fizzbuzz case I don't get why it's preferable to have the arrow over something like
fizzbuzz x = combine (three x) (five x)
Although maybe it's just a case of making it simple to compose functions when there are tens of inputs or something, so the simple case wouldn't really demonstrate anything like that.
If they're meant to be, somebody will cook up a few great instances and show why monads and other abstractions were insufficient. Until that happens I don't think they need to be in people's standard repertoire. Applicative functors seem to be much more handy.
I'd like to know more about comonads too, but the only interesting article I've seen about them I can recall was this one about using comonads to build a zipper for managing location in a site menu.