Reputation: 3496
Following the exercises in the Typeclassopedia, I tried to implement an instance of Functor for Either. My first attempt was the following:
instance Functor (Either a) where
fmap f (Right a) = Right (f a)
fmap _ left = left
This raises the following compile-time error:
functor.hs:7:17:
Couldn't match type ‘a1’ with ‘b’
‘a1’ is a rigid type variable bound by
the type signature for
fmap :: (a1 -> b) -> Either a a1 -> Either a b
at functor.hs:6:3
‘b’ is a rigid type variable bound by
the type signature for
fmap :: (a1 -> b) -> Either a a1 -> Either a b
at functor.hs:6:3
Expected type: Either a b
Actual type: Either a a1
Relevant bindings include
left :: Either a a1 (bound at functor.hs:7:10)
fmap :: (a1 -> b) -> Either a a1 -> Either a b
(bound at functor.hs:6:3)
In the expression: left
In an equation for ‘fmap’: fmap _ left = left
The easiest way to solve this is to replace the second definition of fmap
like the following:
instance Functor (Either a) where
fmap f (Right a) = Right (f a)
fmap _ (Left a) = Left a
Can someone explain me why the error is solved by explicitly pattern-matching in the second definition of fmap
?
Upvotes: 6
Views: 357
Reputation: 19772
Your problem is the third line:
fmap _ left = left
The term left
in the left-hand side has type Either a a1
, and right-hand side is expected to have type Either a b
. Furthermore, a1
and b
are not expected to unify, because the type of fmap
is Functor f => (a1 -> b) -> (f a1 -> f b)
, or, specifically for this instance, (a1 -> b) -> (Either a a1) -> (Either a b)
. Hence the type error.
Upvotes: 5
Reputation: 54078
The reason is that you're changing the type of the Left a
even when you aren't changing the values inside it. Note that for Left 1 :: Either Int String
, fmap length (Left 1)
has type Either Int Int
. Even though only one integer appears in the value of Left 1
, it's type has changed because the other type parameter changed.
This is similar to the following case:
> let x = [] :: [String]
> x == fmap length x
Couldn't match type ‘Int’ with ‘[Char]’
Expected type: [Char] -> String
Actual type: [Char] -> Int
In the first argument of ‘fmap’, namely ‘length’
In the second argument of ‘(==)’, namely ‘fmap length x’
Even though both values are the empty list, the lists have different types. x
has type [String]
and fmap length x
has type [Int]
. Since equality has the type (==) :: Eq a => a -> a -> Bool
, you see that you can't compare values of two different types for equality since it's the same a
.
Upvotes: 10