I also had trouble with this one, and I found Ramon and Mikhail's answers informative -- thanks! I'm putting this in an answer rather than a comment because 500 chars is too short, and for code formatting.
I was having trouble understanding what was covariant about (a -> Int)
and came up with this counterexample showing data K a = K (a -> Int)
can be made an instance of Functor (disproving Ramon's proof)
data K a = K (a -> Int)
instance Functor K where
fmap g (K f) = K (const 0)
If it compiles, it must be correct, right? ;-)
I spent some time trying other permutations. Flopping the function around just made it easier:
-- "o" for "output"
-- The fmapped (1st) type is a function output so we're OK.
data K0 o = K0 (Int -> o)
instance Functor K0 where
fmap :: (oa -> ob) -> (K0 oa) -> (K0 ob)
fmap g (K0 f) = K0 (g . f)
Turning the Int into a type variable reduced it to Section 3.2 exercise 1 part 2:
-- The fmapped (2nd) type argument is an output
data K1 a b = K1 (a -> b)
instance Functor (K1 a) where
fmap :: (b1 -> b2) -> K1 a b1 -> K1 a b2
fmap g (K1 f) = K1 (g . f)
Forcing the fmapped type to be an argument of the function was the key... just like Mikhail's answer said, but now I understand it ;-)
-- The fmapped (2nd) type argument is an input
data K2 a b = K2 (b -> a)
instance Functor (K2 o) where
fmap :: (ia -> ib) -> (K2 o ia) -> (K2 o ib)
-- Can't get our hands on a value of type o
fmap g (K2 f) = K2 (const (undefined :: o))
-- Nor one of type ia
fmap g (K2 f) = K2 (const (f (undefined :: ia)))
(-> int)
? – Ramon Snir((->) Int)
is actually fine, you need something likedata K a = K (a -> Int)
. – Mikhail Glushenkov(+ 1) = \a -> a + 1
. – huon(-> int)
<>((->) int)
. – Ramon Snir