I was looking at the laws for the Alt typeclass, which looks like this:
class Functor f => Alt f
where
(<!>) :: f a -> f a -> f a
One of the laws goes like this:
<$> left-distributes over <!>: f <$> (a <!> b) = (f <$> a) <!> (f <$> b)
More verbosely, this is:
fmap f $ (<!>) a b = (<!>) (fmap f a) (fmap f b)
Let's say we uncurry the <!> operation, i.e. that we suppose the class is written like this:
class Functor f => Alt f
where
alt :: (f a, f a) -> f a
We can write a combinator like this:
mapBoth :: Functor f => (a -> b) -> (f a, f a) -> (f b, f b)
mapBoth f = bimap (fmap f) (fmap f)
This represents the composition of the type Pair a = (a, a) functor with a given functor f. So it is itself the morphism mapping of a functor.
The law in question can now be written (without changing its meaning) like this:
fmap f . alt = alt . mapBoth f
Note that the mapBoth f is simply applying fmap f to both of the arguments of alt, as in the original statement of the law.
This is akin to demanding that alt is a natural transformation from the functor (f -, f -) to the functor f -.
However, isn't it actually impossible for a function of alt's type not to be a natural transformation? How would one write a "bad" implementation of alt that that typechecks, but would be rejected by the law?