8

I have been working through the great good book, but I am struggling slightly with Applicative Functors.

In the following example max is applied to the contents of the two Maybe functors and returns Just 6.

max <$> Just 3 <*> Just 6

Why in the following example is Left "Hello" returned instead of the contents of the Either functors: Left "Hello World"?

(++) <$> Left "Hello" <*> Left " World"
AndrewC
  • 32,300
  • 7
  • 79
  • 115
Jim Jeffries
  • 9,841
  • 15
  • 62
  • 103
  • It's a traditional usage of Either that Right represents a value you're interested in, whereas Left represents a failure. Right (correct) values can be combined and modified using Applicative and Functor, whereas a Left over bad value will stubbornly persist, so it's good for things like reporting the first error the way a simple compiler might. – AndrewC Dec 14 '12 at 18:50

2 Answers2

13

It's because the type parameter in the Functor instance (and Applicative etc.) is the second type parameter. In

Either a b

the a type, and the Left values are not affected by functorial or applicative operations, because they are considered failure cases or otherwise inaccessible.

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

Use Right,

(++)  <$> Right "Hello" <*> Right " World"

to get concatenation.

Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • If you want to flip the type variables, you can use the `Data.EitherR` module from the `errors` package. This provides two options: `flipE`, which flips an argument `Either` or `EitherR` which wraps it in a newtype that swaps the variable order, giving the symmetric `Functor` and `Applicative` instances. – Gabriella Gonzalez Dec 14 '12 at 21:14
11

To add to Daniel's excellent answer, there are a couple points I'd like to make:

First, here's the Applicative instance:

instance Applicative (Either e) where
    pure             =  Right
    Left  e  <*>  _  =  Left e
    Right f  <*>  r  =  fmap f r

You can see that this is 'short-circuiting' -- as soon as it hits a Left, it aborts and returns that Left. You can check this with poor man's strictness analysis:

ghci> (++) <$> Left "Hello" <*> undefined 
Left "Hello"                              -- <<== it's not undefined :) !!

ghci>  (++) <$> Right "Hello" <*> undefined 
*** Exception: Prelude.undefined          -- <<== undefined ... :(

ghci> Left "oops" <*> undefined <*> undefined 
Left "oops"                               -- <<== :)

ghci> Right (++) <*> undefined <*> undefined 
*** Exception: Prelude.undefined          -- <<== :( 

Second, your example is slightly tricky. In general, the type of the function and the e in Either e are not related. Here's <*>s type:

(<*>) :: Applicative f => f (a -> b) -> f a -> f b

If we make the substitution f -->> Either e, we get:

(<*>) :: Either e (a -> b) -> Either e a -> Either e b

Although in your example, e and a match, in general they won't, which means you can't polymorphically implement an Applicative instance for Either e which applies the function to a left-hand argument.

Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192