Reputation: 41909
J. Abrahamson provided an in-depth answer to my composing-monads-v-applicative-functors question.
I gained some intuition, but I do not fully understand his helpful answer.
Given the following Either
's:
scala> x
res0: Either[String,Int] = Right(100)
scala> err
res1: Either[Boolean,Int] = Left(false)
I tried to chain them together:
scala> for {
| xx <- x.right
| yy <- err.right
| } yield xx + yy
res3: scala.util.Either[Any,Int] = Left(false)
But, of course I don't want an Either[Any, Int]
. Yet we get Any
since, as I understand, the parent of Either[String, Int]
and Either[Boolean, Int]
is Either[Any, Int]
.
When building a for-comprehension, is the typical approach to find an end-type, i.e. Either[String, Int]
, and then make each flatMap
call have that type?
Upvotes: 0
Views: 204
Reputation: 22374
If you mean that you want Either[Boolean, Int]
instead of Either[Any, Int]
, then "You can't always get what you want". Composition of same (as in your example) Either
types (but some other values) may return String
instead of Boolean
even for right projection:
scala> val x: Either[String,Int] = Left("aaa")
x: Either[String,Int] = Left(aaa)
scala> val r: Either[Boolean,Int] = Right(100)
r: Either[Boolean,Int] = Right(100)
scala> for {
| xx <- x.right
| yy <- r.right //you may swap xx and yy - it doesn't matter for this example, which means that flatMap could be called on any of it
| } yield yy + xx
res21: scala.util.Either[Any,Int] = Left(aaa)
So, Either[Any,Int]
is really correct and smaller as possible end-type for it.
The desugared version:
scala> x.right.flatMap(xx => r.right.map(_ + xx))
res27: scala.util.Either[Any,Int] = Left(aaa)
Monad's flatMap
signature:
flatMap[AA >: A, Y](f: (B) ⇒ Either[AA, Y]): Either[AA, Y]
Passed types:
flatMap[Any >: String, Int](f: (Int) ⇒ Either[?Boolean?, Int]): Either[Any, String]
AA >: A
is completely legal here as it allows f = r.right.map(_ + xx)
return left type bigger than String
(so ?Boolean?
becomes Any
), otherwise it wouldn't even work. Answering your question, flatMap
can't have AA = Boolean
here, because A
is already at least String
, and as shown in first example can actually be a String
.
And, by the way, there is no monads composition in this example - r
could be just a functor. More than that, you're composing RightProjection
with RightProjection
here, so they commute automatically.
The only way to inferr Boolean
is to kill String
type with Nothing
- you can do that only if you sure that x
is always Right
:
scala> val x: Either[Nothing,Int] = Right(100)
x: Either[Nothing,Int] = Right(100)
scala> val r: Either[Boolean,Int] = Left(false)
r: Either[Boolean,Int] = Left(false)
scala> for {
| yy <- r.right
| xx <- x.right
|
| } yield yy + xx
res24: scala.util.Either[Boolean,Int] = Left(false)
Then, of course you can't put strings, which is totally correct.
Upvotes: 1