In my free time I\'m learning Haskell, so this is a beginner question.
In my readings I came across an example illustrating how Either a
is made an instance
This is right. There is also another quite important reason for this behavior: You can think of Either a b
as a computation, that may succeed and return b
or fail with an error message a
. (This is also, how the monad instance works). So it's only natural, that the functor instance won't touch the Left
values, since you want to map over the computation, if it fails, there's nothing to manipulate.
As others mentioned, Either
type is a functor in its both arguments. But in Haskell we are able to (directly) define only functors in a type's last arguments. In cases like this, we can get around the limitation by using newtype
s:
newtype FlipEither b a = FlipEither { unFlipEither :: Either a b }
So we have constructor FlipEither :: Either a b -> FlipEither b a
that wraps an Either
into our newtype
with swapped type arguments. And we have dectructor unFlipEither :: FlipEither b a -> Either a b
that unwraps it back. Now we can define a functor instance in FlipEither
's last argument, which is actually Either
's first argument:
instance Functor (FlipEither b) where
fmap f (FlipEither (Left x)) = FlipEither (Left (f x))
fmap f (FlipEither (Right x)) = FlipEither (Right x)
Notice that if we forget FlipEither
for a while we get just the definition of Functor
for Either
, just with Left
/Right
swapped. And now, whenever we need a Functor
instance in Either
's first type argument, we can wrap the value into FlipEither
and unwrap it afterward. For example:
fmapE2 :: (a -> b) -> Either a c -> Either b c
fmapE2 f = unFlipEither . fmap f . FlipEither
Update: Have a look at Data.Bifunctor, of which Either
and (,)
are instances of. Each bifunctor has two arguments and is a functor in each of them. This is reflected in Bifunctor
's methods first
and second
.
The definition of Bifunctor
of Either
is very symetric:
instance Bifunctor Either where
bimap f _ (Left a) = Left (f a)
bimap _ g (Right b) = Right (g b)
first f = bimap f id
second f = bimap id f
Now, I'm trying to understand why the implementation maps in the case of a Right value constructor, but doesn't in the case of a Left?
Plug in here and it might make sense.
Assume a = String (an error message) You apply Either a to an Float.
So you have an f: Float -> Integer say for example roundoff.
(Either String) (Float) = Either String Float.
now (fmap f):: Either String Float -> Either String Int So what are you going to do with f? f doesn't have a clue what to do with strings so you can't do anything there. That is obviously the only thing you can act on are the right values while leaving the left values unchanged.
In other words Either a is a functor because there is such an obvious fmap given by:
Your account is right of course. Maybe the reason why we have a difficulty with instances like this is that we are really defining infinitely many functor instances at once -- one for each possible Left
type. But a Functor instance is a systematic way of operating on the infinitely many types in the system. So we are defining infinitely many ways of systematically operating on the infinitely many types in the system. The instance involves generality in two ways.
If you take it by stages, though, maybe it's not so strange. The first of these types is a longwinded version of Maybe
using the unit type ()
and its only legitimate value ()
:
data MightBe b = Nope () | Yep b
data UnlessError b = Bad String | Good b
data ElseInt b = Else Int | Value b
Here we might get tired and make an abstraction:
data Unless a b = Mere a | Genuine b
Now we make our Functor instances, unproblematically, the first looking a lot like the instance for Maybe
:
instance Functor MightBe where
fmap f (Nope ()) = Nope () -- compare with Nothing
fmap f (Yep x) = Yep (f x) -- compare with Just (f x)
instance Functor UnlessError where
fmap f (Bad str) = Bad str -- a more informative Nothing
fmap f (Good x) = Good (f x)
instance Functor ElseInt where
fmap f (Else n) = Else n
fmap f (Value b) = Value (f b)
But, again, why bother, let's make the abstraction:
instance Functor (Unless a) where
fmap f (Mere a) = Mere a
fmap f (Genuine x) = Genuine (f x)
The Mere a
terms aren't touched, as the ()
, String
and Int
values weren't touched.