# How to read arrow combinators

Posted on December 11, 2016 by Kwang Yul Seo
Tags: Arrow

Arrows, like monads and applicative functors, express computations that happen within a context. In Haskell, arrows are represented with a constructor class `Arrow`:

``````class Arrow a where
arr :: (b -> c) -> a b c
-- Each function may be treated as a computation.

(>>>) :: a b c -> a c d -> a b d
-- Computations may be composed, by connecting the output of the first to the input of the second.

first :: a b c -> a (b,d) (c,d)
-- A computation may be applied to part of the input, with the rest copied through to the output.
...``````

As usual, it takes some time to comprehend what arrow is and why and how it is useful. If you are looking for a good arrow tutorial, I strongly recommend John Hughes’s Programming with Arrows.

However, it is not necessary to understand arrow just to read some code written with arrow combinators because arrows are often used even when there is no compelling reason to do so. `(->)` is an instance of `Arrow` and some Haskell programmers just prefer arrow style even though combinators of `Data.Bifunctor` are often more readable.

``````instance Arrow (->) where
arr f = f
(***) f g ~(x,y) = (f x, g y)``````

So here is my tip. If you encounter code written with arrow combinators in the context of `(->)`, use the following translation table to decode what the code means.

Combinator Meaning Alternatives
(>>>) flip (.)
first (x, y) -> (f x, y) first (Data.Bifunctor)
second (x, y) -> (x, f y) second (Data.Bifunctor)
(***) g (x, y) -> (f x, g y) bimap (Data.Bifunctor)
(&&&) g x -> (f x, g x) liftA2 (,) (Control.Applicative)
left Maps over Left case first (Data.Bifunctor)
right Maps over Right case second (Data.Bifunctor)
(+++) Maps over both cases bimap (Data.Bifunctor)
(|||) Eliminates Either either (Data.Either)
app (f, x) -> f x uncurry (\$)

In case of `second` and `right`, we can also use `fmap` because both `(,)` and `Either` is functorial on the second type. But I personally don’t recommend `fmap` in this case because these types arbitrarily map the second type instead of the first one.