There is a bit of overlap between Bifunctor and Arrow methods:
class Bifunctor p where
first :: (a -> a') -> p a b -> p a' b
second :: (b -> b') -> p a b -> p a b'
bimap :: (a -> a') -> (b -> b') -> p a b -> p a' b'
class Arrow (~~>) where
...
first :: (a ~~> a') -> (a, b) ~~> (a', b)
second :: (b ~~> b') -> (a, b) ~~> (a, b')
(***) :: (a ~~> a') -> (b ~~> b') -> (a, b) ~~> (a', b')
The Bifunctor class comes with laws completely analogous to those of Functor.
The Arrow class comes with a number of laws different laws and a somewhat cryptic warning about (***): "Note that this is in general not a functor." Surprisingly (to me) there's only one law about (***):
first f >>> arr (id *** g) = arr (id *** g) >>> first f
The Arrow (->) instance and the Bifunctor (,) instance match up exactly, so that bimap @(,) = (***) @(->). Is there some special significance to this? Is there a meaningful hypothetical
class Foo (~~>) p where
biFoo :: (a ~~> a') -> (b ~~> b') -> p a b ~~> p a' b'
If so, does that admit functional dependencies?
Arrowis a (somewhat bastardized) precursor to a class of cartesian closed categories, or a least cartesian monoidal categories. Specifically, to monoidal categories whose tensor product is(,)and unit element().Recall that a monoidal category is characterised by the tensor product as a bifunctor, so there's your connection between
ArrowandBifunctor.***has in fact more laws than you listed, only, the library chooses to formulate those in terms offirstinstead. Here's an equivalent definition of the class:Incidentally, it is also possible to remove the
arrfor lifting pure functions, and instead give the superclass only the dedicated methodsfst,sndandassoc. I call that classCartesian. This allows defining “arrow” categories that don't contain arbitrary Haskell functions; linear maps are an important example.