I'm new-ish to functional programming, and I have a question about a description from the Either documentation that I'm having a really hard time wrapping my mind around.
I understand that an Either
is kind of like an evolution to the Option
in that an Either
represents happy and sad path values with Right
and Left
similar to how an Option represents the presence or absence of a value with Some
and None
. I also understand that an Option
is a special case of List
, one containing either 0 or 1 values, which is why we see so many List
-ish methods (map
, filter
, fold
, etc) on the Option
type, and by extension, on the Either
type as well.
I also understand that, at some point, Either
s were intentionally 'right-biased' so that they could be given given a flatMap
method, and that this method allows us to transform an Either[A, B]
into an Either[C, D]
, which allows us to chain various Either
computations together and this is the basis for why we're able to use Either
s in for-comprehensions.
Every time I visit the Either
documentation, I see that the flatMap
method has the description "Binds the given function across Right", and I have never been able to figure out what this description means. Can anyone explain it to me?
and that this method allows us to transform an
Either[A, B]
into anEither[C, D]
This part isn't quite right. While it is true that, if we have A => C
and B => D
, then we can convert Either[A, B]
into Either[C, D]
, that's not given to us by the "generalization of Option
" functions you're talking about. When we speak of Either
as an error-handling type (where, by convention, the error type is the left-hand argument and the "happy path" is the right-hand argument), we generally hold the left-hand argument constant. So the type we're looking at is Either[A, _]
, where _
can change but A
is some fixed error type.
Then we get
def map[B1](f: (B) => B1): Either[A, B1]
That is, our map
is right-biased. It assumes the left-hand argument (the "sad path") remains the same and only the right-hand argument changes. This isn't a theoretical constraint; we could just as easily have defined map
to work on the left argument. But we didn't.
Now the word "bind", in particular, comes from Haskell. In particular, it's the usual way to pronounce the (>>=)
operator defined on the Monad
typeclass.
(>>=) :: m b -> (b -> m b') -> m b'
I won't get into all of the category-theoretical goodies, except to say that what Haskell calls (>>=)
(and pronounces "bind") is what Scala calls flatMap
. On Either
in Haskell, (>>=)
looks like this.
(>>=) :: Either a b -> (b -> Either a b') -> Either a b'
And flatMap
on Scala's Either
is defined as[1]
def flatMap[B1](f: (B) => Either[A, B1]): Either[A, B1]
In both cases, we take an Either[A, B]
and a way to go from B
to Either[A, B1]
, and we produce an Either[A, B1]
. This operation is called "binding" the Either
. And since we're binding it on the right-hand argument instead of the left-hand one (again, purely a convention, but one we choose and stick to consistently), we say we're binding on the right.
[1] Okay, I lied a little. The full Scala type signature is
def flatMap[A1 >: A, B1](f: (B) => Either[A1, B1]): Either[A1, B1]
This is covering for variance annotations. A
is covariant in Either[A, B]
, so strictly speaking we shouldn't be able to have Either[A, B1]
in argument position for flatMap
. But we can do this little incantation and have "Either[A1, B1]
for some A1 >: A
" as an argument to get basically the same idea.