Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make Either into a functor in second type

I have been reading "Learn You A Haskell For Great Good!" and right now I am on "The Functor Typeclass" section.

Here they make Either into a functor by fixing the first type as follows:

instance Functor (Either a) where
  fmap f (Right x) = Right (f x)
  fmap f (Left x)  = Left x

So I wanted to ask, how could I make Either into a functor in the first type (by fixing the second type) instead, so that, I get the following definition for fmap

fmap f (Left x) = Left (f x)
fmap f (Right x)  = Right x
like image 836
kishlaya Avatar asked May 09 '18 12:05

kishlaya


1 Answers

You can't; Either a can be a functor because the partial application of Either has kind * -> *, but you can't do partial application from the right.

Instead, you might be interested in the Bifunctor instance of Either:

instance Bifunctor Either where
    bimap f _ (Left a) = Left (f a)
    bimap _ g (Right b) = Right (g b)

bimap takes two functions, one for each of the two types wrapped by Either.

> bimap (+1) length (Left 3)
Left 4
> bimap (+1) length (Right "hi")
Right 2

There are also first and second functions that focus on one or the other type. second corresponds to the regular fmap for Either; first is the function you are looking for.

> first (+1) (Left 3)
Left 4
> first (+1) (Right 3)
Right 3
> second (+1) (Left 3)
Left 3
> second (+1) (Right 3)
Right 4

(hat tip to @leftaroundabout)

The Control.Arrow module provides the left function, which effectively is the same as second, but with a more descriptive name and a different derivation. Compare their types:

> :t Data.Bifunctor.second
Data.Bifunctor.second :: Bifunctor p => (b -> c) -> p a b -> p a c
> :t Control.Arrow.left
Control.Arrow.left :: ArrowChoice a => a b c -> a (Either b d) (Either c d)

second is hard-coded to work with functions and can be restricted by p ~ Either. left is hard-coded to work with Either and can be restricted by a ~ (->).


Confusingly, Control.Arrow also provides a second function which is similar to the Bifunctor instance for tuples:

> :t Control.Arrow.second
Control.Arrow.second :: Arrow a => a b c -> a (d, b) (d, c)

> Control.Arrow.second (+1) (1,2) == Data.Bifunctor.second (+1) (1,2)
True
like image 117
chepner Avatar answered Nov 05 '22 11:11

chepner