Arrows
Function compositions with multiple channels
Arrow
is, vaguely speaking, the class of morphisms that compose like functions, with both serial composition and “parallel composition”. While it is most interesting as a generalisation of functions, the Arrow (->)
instance itself is already quite useful. For instance, the following function:
spaceAround :: Double -> [Double] -> Double
spaceAround x ys = minimum greater - maximum smaller
where (greater, smaller) = partition (>x) ys
can also be written with arrow combinators:
spaceAround x = partition (>x) >>> minimum *** maximum >>> uncurry (-)
This kind of composition can best be visualised with a diagram:
──── minimum ────
╱ * ╲
──── partition (>x) >>> * >>> uncurry (-) ───
╲ * ╱
──── maximum ────
Here,
-
The
>>>
operator is just a flipped version of the ordinary.
composition operator (there’s also a<<<
version that composes right-to-left). It pipes the data from one processing step to the next. -
the out-going
╱
╲
indicate the data flow is split up in two “channels”. In terms of Haskell types, this is realised with tuples:partition (>x) :: [Double] -> ([Double], [Double])
splits up the flow in two
[Double]
channels, whereasuncurry (-) :: (Double,Double) -> Double
merges two
Double
channels. -
***
is the parallel† composition operator. It letsmaximum
andminimum
operate independently on different channels of the data. For functions, the signature of this operator is(***) :: (b->c) -> (β->γ) -> (b,β)->(c,γ)
†At least in the Hask category (i.e. in the Arrow (->)
instance), f***g
does not actually compute f
and g
in parallel as in, on different threads. This would theoretically be possible, though.