Apologies in advance for the quality of the html blogger seems to be generating. I probably need to find a better solution (like using lhs and attaching pdfs or something).
In the recent discussion about run length encoding, one thing people focused on was the use of the &&& operator and how noone understands arrows so &&& is scary and arcane.
I feel perfectly placed to comment on this, because I don’t understand arrows either. :-) I cheerfully abuse the arrow operators for their function instance without really worrying about that, and it seems to work fine.
So, this is just a rundown of what the arrow operations do for the function instance, in order to make them seem less scary. I might follow up with looking at Kleisli arrows later (which are arrows that arise from monads in a natural way).
The arrow class is defined as follows:
class Arrow a where arr :: (b -> c) -> a b c pure :: (b -> c) -> a b c (>>>) :: a b c -> a c d -> a b d first :: a b c -> a (b, d) (c, d) second :: a b c -> a (d, b) (d, c) (***) :: a b c -> a b' c' -> a (b, b') (c, c') (&&&) :: a b c -> a b c' -> a b (c, c')
The first two are totally uninteresting for functions (they’re just the identity). So if we cut those out and specialise the signatures to functions we get the following:
(>>>) :: (b -> c) -> (c -> d) -> (b -> d) first :: (b -> c) -> (b, d) -> (c, d) second :: (b -> c) -> (d, b) -> (d, c) (***) :: (b -> c) -> (b' -> c') -> (b, b') -> (c, c') (&&&) :: (b -> c) -> (b -> c') -> b -> (c, c')
i.e. these are basically all operators for manipulating combinations of pairs and functions. Most of these you can probably infer the purpose of from their types and names, but I’ll go through them anyway. We’ve already encountered &&&, and it’s the one I use the most, so I’ll go through these in reverse order.
The &&& operator is in fact very simple, but it’s the arrow operator I use the most. Consider the following:
> (+1) &&& (+2) $ 3 (4, 5)
i.e. f &&& g when applied to x just evaluates both and returns them as a pairs of both results.
We could define this as:
(&&&) :: (a -> b) -> (a -> c) -> (a -> (b, c)) (f &&& g) x = (f x, g x)
For example, in the run length encoding article we had the function head &&& length:
> head &&& length $ [1, 3] (1,2)
In a similar usage to the RLE article, I’ve often found the following sort of trick useful:
map (head &&& length) . group . sort
This counts frequencies of elements in a list.
> map (head &&& length) . group . sort $ ["foo", "bar", "foo", "baz"] [("bar",1),("baz",1),("foo",2)]
> (+1) *** (+2) $ (3, 4) (4, 6)
i.e. f *** g applied to (x, y) returns (f x, g y). Or, in a more readable form:
(***) :: (b -> c) -> (b' -> c') -> (b, b') -> (c, c') (f *** g) (x, y) = (f x, g y)
I don’t really have an obvious use case for this one. I’m sure they exist though.
I’m going to pick up the speed now, as I’m getting bored and so you probably are too. :-)
(>>>) :: (b -> c) -> (c -> d) -> (b -> d) (f >>> g) x = g (f x)
Or in other words, >>> is just reverse function composition. So we could have written this as
(>>>) :: (b -> c) -> (c -> d) -> (b -> d) (f >>> g) x = g . f
(>>>) :: (b -> c) -> (c -> d) -> (b -> d) (>>>) = flip (.)
Now for first and second:
first :: (b -> c) -> (b, d) -> (c, d) first f (x, y) = (f x, y) second :: (b -> c) -> (d, b) -> (d, c) second f (x, y) = (x, f y)
i.e. These just take a function and apply it to the first or second entry of a tuple, leaving the other unchanged.
Those are all the core arrow operations. There are a few derived operations, but the only one which is at all interesting for functions is <<<, which is just function composition. Another Arrow related class which functions are an instance of is ArrowChoice. ArrowChoice does for Either what Arrow does for pairs (blah blah, category theory, blah, (,) and Either are dual, blah). Here's the instance declaration:
class Arrow a => ArrowChoice a where left :: a b c -> a (Either b d) (Either c d) right :: a b c -> a (Either d b) (Either d c) (+++) :: a b c -> a b’ c’ -> a (Either b b’) (Either c c’) (|||) :: a b d -> a c d -> a (Either b c) d
Specialised to functions:
left :: (b -> c) -> (Either b d) -> (Either c d) right :: (b -> c) -> (Either d b) -> (Either d c) (+++) :: (b -> c) -> (b' -> c') -> (Either b b') -> (Either c c') (|||) :: (b -> d) -> (c -> d) -> (Either b c) -> d
Lets jump straight to writing out some definitions:
left :: (b -> c) -> (Either b d) -> (Either c d) left f (Left x) = Left $ f x left _ (Right y) = Right y right :: (b -> c) -> (Either d b) -> (Either d c) right f (Right x) = Right $ f x right _ (Left y) = Left y (+++) :: (b -> c) -> (b' -> c') -> (Either b b') -> (Either c c') (f +++ g) (Left x) = Left $ f x (f +++ g) (Right x) = Right $ g x (|||) :: (b -> d) -> (c -> d) -> (Either b c) -> d (f ||| g) (Left x) = f x (f ||| g) (Right x) = g x
i.e. left f applies f to the left option of an Either and leaves the right option alone.
> left (+1) $ Left 2 Left 3 > left (+1) $ Right 2 Right 2
right does the reverse.
f +++ g applies f to the left option and g to the right:
> (+1) +++ (+2) $ Left 2 Left 3 > (+1) +++ (+2) $ Right 2 Right 4
So we could have implemented left and right (and indeed, this is how they’re actually implemented in the real instance declaration) as follows:
left :: (b -> c) -> (Either b d) -> (Either c d) left f = f +++ id right :: (b -> c) -> (Either d b) -> (Either d c) right f = id +++ f
Or even as (+++id) and (id+++).
Finally, (f ||| g) x is basically just a combinator for case matching, applying f if we have a Left value, g if we have a Right. It’s just the standard ‘either’ function but it can be nice to have it available as an operator.
> (+1) ||| (+2) $ Left 2 3 > (+1) ||| (+2) $ Right 2 4
So, there you have it. Arrow operations. Not too scary, and now you have a bunch of new combinators to play with. I doubt it will revolutionise your Haskell code, but every now and then they allow for a really neat solution you wouldn’t otherwise have thought of. Have fun.
Great article. Just one question, though: wouldn’t the type signature for &&& be more idiomatically written as: (&&&) :: (a -> b) -> (a -> c) -> a -> (b, c) ? I’m not saying they’re not equivalent, just that the grouping is usually left implicit.
You’re quite right. My Haskell isn’t always entirely idiomatic. :-)
I’m rather more fond of the humble bracket than the average Haskellista, and tend to include more than are necessary purely out of habit.
(I actually did write it the way you suggested in the bit following the class definition, but seem to have reverted to my normal style in the implementation. This caused me a lot of confusion as I was staring at the first part wondering what on earth you were talking about. :-) )
“I don’t really have an obvious use case for this one. I’m sure they exist though.”
Generally I use a lot *** and &&& together, to avoid to call the pair explicitly.
For example, to get the length of a list in a very crude catamorphism style.
Some descontruct and construct functions
deconstruct  = Left ()
deconstruct l = Right . ( head &&& tail) $ l
construct = const 0 ||| uncurry (+) . (const 1 *** id)
And the length function
myLenght = construct . ( id +++ ( id *** myLength)) . deconstruct
Sorry for bad english…
“I’m rather more fond of the humble bracket than the average Haskellista”
But not *that* fond. :-)
Thanks David, that is extremely useful.
BTW, how do people pronounce the operators, when the need arises to utter their names? “arrow-and” etc?
I usually pronounce &&& as “Split”, *** as “parallel with” and >>> as just “arrowed”
So “id &&& (+1) >>> ((*2) *** abs)”
would become “id split add-one arrowed times-two parallel with abs” in my head.