Best practices for Arrows?

I feel I may be doing a lot of programming with Arrows in the near future. Currently I'm delighted that Arrow notation[1] exists. It makes using Arrows much less painful. Are there any best-practices I should be aware of with Arrows? Or is it just a case of getting on with it? Tom 1. http://www.haskell.org/ghc/docs/latest/html/users_guide/arrow-notation.html

Tom Ellis
Are there any best-practices I should be aware of with Arrows? Or is it just a case of getting on with it?
The best practice is probably to avoid them. If your type is a monad, there is little reason to use the awkward arrow interface. In some cases the arrow interface can improve the asymptotic performance though, for example in the case of `Auto` (which is actually a monad, but the monadic interface introduces a space leak). In most cases when you expose an `Arrow` interface you can also expose a `Category`+`Applicative` interface, which is pretty much equivalent (except for a few extra laws): proc x -> do y1 <- a1 -< x y2 <- a2 -< x id -< x + y1 + y2^2 Is equivalent to: liftA3 (\x y1 y2 -> x + y1 + y2^2) id a1 a2 All arrows give rise to a [Profunctor] instance, so instead of `arr` you can use `lmap` and `rmap`/`fmap`: arr f . c = fmap f c c . arr f = lmap f c If the interface is not under your control, make yourself comfortable with the complete arrow syntax, most notably how it handles operators, combinators and the `(| banana bracket notation |)`. This is very valuable information. Try to separate individual computations as much as possible and compose using `(.)` (or `(<<<)`/`(>>>)` if you prefer). This makes your code much more readable: c = a . b . c where a = {- ... -} b = {- ... -} c = {- ... -} There is one case where the arrow notation is really indispensable: value recursion via `ArrowLoop`: proc _ -> do rec v <- integral -< x + 1 x <- integral -< v id -< (x, v) Here the position x is the integral of the velocity, which is itself the integral of the position + 1. This is awkward to express in terms of `loop`, so arrow notation is really a big helper here. [Profunctor]: http://hackage.haskell.org/package/profunctors Greets, Ertugrul -- Not to be or to be and (not to be or to be and (not to be or to be and (not to be or to be and ... that is the list monad.

Hi Ertugul. Thanks for taking the time to write me an in-depth reply! I have a few comments and a question. On Sat, Jun 22, 2013 at 03:36:15PM +0200, Ertugrul Söylemez wrote:
Tom Ellis
wrote: Are there any best-practices I should be aware of with Arrows? Or is it just a case of getting on with it?
The best practice is probably to avoid them. If your type is a monad, there is little reason to use the awkward arrow interface.
Unfortunately my type doesn't have a Monad instance.
In most cases when you expose an `Arrow` interface you can also expose a `Category`+`Applicative` interface, which is pretty much equivalent (except for a few extra laws):
proc x -> do y1 <- a1 -< x y2 <- a2 -< x id -< x + y1 + y2^2
Is equivalent to:
liftA3 (\x y1 y2 -> x + y1 + y2^2) id a1 a2
Yes, I can see how that would be useful. My question is: are you talking about this Applicative instance: data MyArr a b = ... instance Arrow MyArr where ... instance Functor (MyArr a) where fmap f = (arr f <<<) instance Applicative (MyArr a) where pure = arr . const f <*> g = arr (uncurry ($)) <<< (f &&& g)
If the interface is not under your control, make yourself comfortable with the complete arrow syntax, most notably how it handles operators, combinators and the `(| banana bracket notation |)`. This is very valuable information.
Interesting. I hadn't noticed the `(| banana bracket notation |)` on the GHC Arrows page[1] before, but just saw it when I went back to check.
Try to separate individual computations as much as possible and compose using `(.)` (or `(<<<)`/`(>>>)` if you prefer). This makes your code much more readable:
Yes, agreed. I'm a strong proponent of using (.) for functions and (<=<) when dealing with Monads.
There is one case where the arrow notation is really indispensable: value recursion via `ArrowLoop`: [...]
I think I will be able to make my Arrow an ArrowLoop, but I haven't checked. Thanks again, Tom [1] http://www.haskell.org/ghc/docs/latest/html/users_guide/arrow-notation.html

On Sat, Jun 22, 2013 at 07:05:09PM +0100, Tom Ellis wrote:
On Sat, Jun 22, 2013 at 03:36:15PM +0200, Ertugrul Söylemez wrote:
If the interface is not under your control, make yourself comfortable with the complete arrow syntax, most notably how it handles operators, combinators and the `(| banana bracket notation |)`. This is very valuable information.
Interesting. I hadn't noticed the `(| banana bracket notation |)` on the GHC Arrows page[1] before, but just saw it when I went back to check.
The banana brackets can be handy when you have operations on your arrow type beyond the ones in Arrow and ArrowLoop. But beware that the types of the operations you can use with it will be changing in the next release of GHC. The change is the first one described on http://hackage.haskell.org/trac/ghc/wiki/ArrowNotation

On Sat, Jun 22, 2013 at 10:24:23PM +0100, Ross Paterson wrote:
On Sat, Jun 22, 2013 at 07:05:09PM +0100, Tom Ellis wrote:
On Sat, Jun 22, 2013 at 03:36:15PM +0200, Ertugrul Söylemez wrote:
If the interface is not under your control, make yourself comfortable with the complete arrow syntax, most notably how it handles operators, combinators and the `(| banana bracket notation |)`. This is very valuable information.
Interesting. I hadn't noticed the `(| banana bracket notation |)` on the GHC Arrows page[1] before, but just saw it when I went back to check.
The banana brackets can be handy when you have operations on your arrow type beyond the ones in Arrow and ArrowLoop. But beware that the types of the operations you can use with it will be changing in the next release of GHC. The change is the first one described on
Is there any example of using Arrow banana brackets? I downloaded all of Hackage and grepped it, and the only usage was in the comments of Control.Arrow.Internals! I have feeling these might be useful to me, but I can't quite understand how they work. Tom

Tom Ellis
Unfortunately my type doesn't have a Monad instance.
If you could reveal the type, we could give more precise suggestions.
In most cases when you expose an `Arrow` interface you can also expose a `Category`+`Applicative` interface, which is pretty much equivalent (except for a few extra laws):
proc x -> do y1 <- a1 -< x y2 <- a2 -< x id -< x + y1 + y2^2
Is equivalent to:
liftA3 (\x y1 y2 -> x + y1 + y2^2) id a1 a2
Yes, I can see how that would be useful. My question is: are you talking about this Applicative instance:
data MyArr a b = ...
instance Arrow MyArr where ...
instance Functor (MyArr a) where fmap f = (arr f <<<)
instance Applicative (MyArr a) where pure = arr . const f <*> g = arr (uncurry ($)) <<< (f &&& g)
Yes, that seems right.
I think I will be able to make my Arrow an ArrowLoop, but I haven't checked.
It's not that your type may have a useful or even sensible ArrowLoop notion, but if it does, then arrow notation is very useful. Also as Ross noted it gives you access to some additional convenience syntax, in particular if your type is an ArrowChoice. Those combinators are extremely awkward to use directly, but proc notation allows you to use regular if/case syntax. However, even then sometimes it can be beneficial to use composition of small self-contained arrow formulas. Greets, Ertugrul -- Not to be or to be and (not to be or to be and (not to be or to be and (not to be or to be and ... that is the list monad.
participants (3)
-
Ertugrul Söylemez
-
Ross Paterson
-
Tom Ellis