简体   繁体   中英

For Comprehension Example to Understand Composing Monads

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, ie Either[String, Int] , and then make each flatMap call have that type?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM