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.