--- Any instance must define either 'arr' or 'pure' (which are synonyms),
--- as well as '>>>' and 'first'. The other combinators have sensible
--- default definitions, which may be overridden for efficiency.
-
-class Arrow a where
-
- -- | Lift a function to an arrow: you must define either this
- -- or 'pure'.
- arr :: (b -> c) -> a b c
- arr = pure
-
- -- | A synonym for 'arr': you must define one or other of them.
- pure :: (b -> c) -> a b c
- pure = arr
-
- -- | Left-to-right composition of arrows.
- (>>>) :: a b c -> a c d -> a b d
-
- -- | Send the first component of the input through the argument
- -- arrow, and copy the rest unchanged to the output.
- first :: a b c -> a (b,d) (c,d)
-
- -- | A mirror image of 'first'.
- --
- -- The default definition may be overridden with a more efficient
- -- version if desired.
- second :: a b c -> a (d,b) (d,c)
- second f = arr swap >>> first f >>> arr swap
- where swap ~(x,y) = (y,x)
-
- -- | Split the input between the two argument arrows and combine
- -- their output. Note that this is in general not a functor.
- --
- -- The default definition may be overridden with a more efficient
- -- version if desired.
- (***) :: a b c -> a b' c' -> a (b,b') (c,c')
- f *** g = first f >>> second g
-
- -- | Fanout: send the input to both argument arrows and combine
- -- their output.
- --
- -- The default definition may be overridden with a more efficient
- -- version if desired.
- (&&&) :: a b c -> a b c' -> a b (c,c')
- f &&& g = arr (\b -> (b,b)) >>> f *** g
+--
+-- Minimal complete definition: 'arr' and 'first'.
+--
+-- The other combinators have sensible default definitions,
+-- which may be overridden for efficiency.
+
+class Category a => Arrow a where
+
+ -- | Lift a function to an arrow.
+ arr :: (b -> c) -> a b c
+
+ -- | Send the first component of the input through the argument
+ -- arrow, and copy the rest unchanged to the output.
+ first :: a b c -> a (b,d) (c,d)
+
+ -- | A mirror image of 'first'.
+ --
+ -- The default definition may be overridden with a more efficient
+ -- version if desired.
+ second :: a b c -> a (d,b) (d,c)
+ second f = arr swap >>> first f >>> arr swap
+ where
+ swap :: (x,y) -> (y,x)
+ swap ~(x,y) = (y,x)
+
+ -- | Split the input between the two argument arrows and combine
+ -- their output. Note that this is in general not a functor.
+ --
+ -- The default definition may be overridden with a more efficient
+ -- version if desired.
+ (***) :: a b c -> a b' c' -> a (b,b') (c,c')
+ f *** g = first f >>> second g
+
+ -- | Fanout: send the input to both argument arrows and combine
+ -- their output.
+ --
+ -- The default definition may be overridden with a more efficient
+ -- version if desired.
+ (&&&) :: a b c -> a b c' -> a b (c,c')
+ f &&& g = arr (\b -> (b,b)) >>> f *** g
+
+{-# RULES
+"compose/arr" forall f g .
+ (arr f) . (arr g) = arr (f . g)
+"first/arr" forall f .
+ first (arr f) = arr (first f)
+"second/arr" forall f .
+ second (arr f) = arr (second f)
+"product/arr" forall f g .
+ arr f *** arr g = arr (f *** g)
+"fanout/arr" forall f g .
+ arr f &&& arr g = arr (f &&& g)
+"compose/first" forall f g .
+ (first f) . (first g) = first (f . g)
+"compose/second" forall f g .
+ (second f) . (second g) = second (f . g)
+ #-}