0

I am trying to understand why do applicative functors work by default (no implementation needed) for some functors like Maybe but for others don't:

Example:
Just (+3) <*> (Just 3) works fine "out of the box"- > 6
Left (+3) <*> Left 3 does not work
Just (+3) <*> Left 4 does not work even if i declare an Either Int Int.

I assume in 99% of cases when dealing with pairs of : (f (a->b) , f a) you must implement the desired behaviour yourself (Cartesian Product (f (a->b)) X (f a) ) and the first example is just something simple out of the box.

Example In the case of (Maybe (a->b) , Either c d) we would need to cover all 4 cases:
Just - Left Just - Right Nothing - Left Nothing -Right

Am i right in this assumption ?

Bercovici Adrian
  • 8,794
  • 17
  • 73
  • 152
  • 6
    `Right (+3) <*> Right 3` works. `Left` doesn't contain the type that the functor operates on. Furthermore, `Just (+3)` and `Left 4` are completely incompatible types. – AJF Mar 29 '19 at 14:51
  • 1
    So the the functor that contains the function has to be the same type as the functor that has the value ? – Bercovici Adrian Mar 29 '19 at 15:02
  • 2
    Right. More generally, when substituting a concrete type for a type variable (`f`, `a`, `b`), all occurrences of the variable get replaced with the same value. – bergey Mar 29 '19 at 15:26
  • 3
    @BercoviciAdrian Yes, precisely. You can use `<*>` to combine `Either String (Int -> Bool)` and `Either String Int` into a `Etiher String Bool`. The `Either String` part must be the same: it's the same functor that is it used for the two arguments and the result type. Indeed, `(<*>) :: f (a -> b) -> f a -> f b` uses the same applicative functor `f` three times (e.g. `f = Either String`). – chi Mar 29 '19 at 15:50
  • 2
    Look at the type for `<*>`: `Applicative f => f (a -> b) -> f a -> f b`. The same applicative functor is used throughout. What did you expect the result of `Just (+3) <*> Left 4` to be, `Just 7` or `Left 7`? – chepner Mar 29 '19 at 17:29

1 Answers1

7

The Applicative instance for Either is defined as:

instance Applicative (Either e) where ...

given the type of (<*>) is Applicative f => f (a -> b) -> f a -> f b for Either that is:

Either e (a -> b) -> Either e a -> Either e b

The type of Left is e -> Either e a so the type of Left (+3) is

Num a => Either (a -> a) b

and the type of Left 3 is:

Num a => Either a b

which leads to the type for Left (+3) <*> Left 3 as (Num a, Num (a -> a)) => Either (a -> a) b which is unlikely to be what you intended.

Since it's the type b which contains the function and value to operate on, using the Right constructor does work:

Right (+3) <*> Right 3
=> Right 6
Lee
  • 142,018
  • 20
  • 234
  • 287
  • 1
    There's a potential for confusion where you write "does not match the required type". These types do unify, as `Num n => Either (n -> n) (a -> b)` even though the Applicative instance doesn't do what the OP expected. – bergey Mar 29 '19 at 15:25
  • Not only that, but `Left (+3) <*> Left 3` is an error because `3` and `(+3)` don't have the same type (unless you declare a `Num` instance for something like `(Num a) => a -> a`) – Robin Zigmond Mar 29 '19 at 15:39