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?