Brent Yorgey\'s Typeclassopedia gives the following exercise:
Give an example of a type of kind
* -> *
which cannot be made an instance
Let's talk about variances.
Here's the basic notion. Consider the type A -> B
. What I want you to imagine is that such a type is similar to "having a B
" and also "owing an A
". In fact, if you pay back your A
you immediately receive your B
. Functions are kind of like escrow in that way.
The notion of "having" and "owing" can extend to other types. For instance, the simplest container
newtype Box a = Box a
behaves like this: if you "have" a Box a
then you also "have" an a
. We consider types which have kind * -> *
and "have" their argument to be (covariant) functors and we can instantiate them to Functor
instance Functor Box where fmap f (Box a) = Box (f a)
What happens if we consider the type of predicates over a type, like
newtype Pred a = Pred (a -> Bool)
in this case, if we "have" a Pred a
, we actually "owe" an a
. This arises from the a
being on the left side of the (->)
arrow. Where fmap
of Functor
is defined by passing the function into the container and applying it to all the places where we "have" our inner type, we can't do the same for Pred a
since we don't "have" and a
s.
Instead, we'll do this
class Contravariant f where
contramap :: (a -> b) -> (f b -> f a)
Now that contramap
is like a "flipped" fmap
? It will allow us to apply the function to the places where we "own" a b
in Pred b
in order to receive a Pred a
. We might call contramap
"barter" because it encodes the idea that if you know how to get b
s from a
s then you can turn a debt of b
s into a debt of a
s.
Let's see how it works
instance Contravariant Pred where
contramap f (Pred p) = Pred (\a -> p (f a))
we just run our trade using f
prior to passing it on into the predicate function. Wonderful!
So now we have covariant and contravariant types. Technically, these are known as covariant and contravariant "functors". I'll also state immediately that almost always a contravariant functor is not also covariant. This, thus, answers your question: there exist a bunch of contravariant functors which are not able to be instantiated to Functor
. Pred
is one of them.
There are tricky types which are both contravariant and covariant functors, though. In particular, the constant functors:
data Z a = Z -- phantom a!
instance Functor Z where fmap _ Z = Z
instance Contravariant Z where contramap _ Z = Z
In fact, you can essentially prove that anything which is both Contravariant
and Functor
has a phantom parameter.
isPhantom :: (Functor f, Contravariant f) => f a -> f b -- coerce?!
isPhantom = contramap (const ()) . fmap (const ()) -- not really...
On the other hand, what happens with a type like
-- from Data.Monoid
newtype Endo a = Endo (a -> a)
In Endo a
we both owe and receive an a
. Does that mean we're debt free? Well, no, it just means that Endo
wants to be both covariant and contravariant and does not have a phantom parameter. The result: Endo
is invariant and can instantiate neither Functor
nor Contravariant
.