Search code examples
haskellfunctoreither

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

Solution

  • 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