2015-04-01 41 views
1

給定一個Either[String,Int]轉換`flatMap`到`換comprehension`無論使用哪種

scala> val z: Either[String, Int] = Right(100) 
z: Either[String,Int] = Right(100) 

我可以寫與flatMap下面的代碼:

scala> z.right.flatMap(x => if(x == 100) Left("foo") else Right(x)) 
res14: scala.util.Either[String,Int] = Left(foo) 

但是,我在做什麼毛病for comprehension版本?

scala> for { 
    | a <- z.right 
    | _ <- if(a == 100) Left("foo") else Right(a) 
    | } yield a 
<console>:11: error: value map is not a member of Product with Serializable 
     with scala.util.Either[String,Int] 
       _ <- if(a == 100) Left("foo") else Right(a) 
        ^

回答

3

if(a == 100) Left("foo") else Right(a)Either[String, Int],而不是LeftProjectionRightProjection,所以它並沒有mapflatMap。您需要將其投影以及:

for { 
    a <- z.right 
    _ <- (if(a == 100) Left("foo") else Right(a)).right 
} yield a 

這之間的差異和一個班輪是,一個班輪等同於:

for { 
    a <- z.right 
} yield (if(a == 100) Left("foo") else Right(a)) 

..這不有一個額外的map最後。

0

我認爲這個問題已被接受的答案已經過時了。

既然斯卡拉2.12,Either默認是正確的偏見。 所以Either已經定義了mapflatMap

/** Binds the given function across `Left`. 
* 
* {{{ 
* Left(12).left.flatMap(x => Left("scala")) // Left("scala") 
* Right(12).left.flatMap(x => Left("scala")) // Right(12) 
* }}} 
* @param f The function to bind across `Left`. 
*/ 
def flatMap[A1, B1 >: B](f: A => Either[A1, B1]): Either[A1, B1] = e match { 
    case Left(a) => f(a) 
    case _  => e.asInstanceOf[Either[A1, B1]] 
} 

/** Maps the function argument through `Left`. 
* 
* {{{ 
* Left(12).left.map(_ + 2) // Left(14) 
* Right[Int, Int](12).left.map(_ + 2) // Right(12) 
* }}} 
*/ 
def map[A1](f: A => A1): Either[A1, B] = e match { 
    case Left(a) => Left(f(a)) 
    case _  => e.asInstanceOf[Either[A1, B]] 
} 

因此,你不必使用RightProjection因爲Either是正確的默認偏見。所以下面的代碼工作。

val z: Either[String, Int] = Right(100) 

    val ans = for { 
    a <- z 
    _ <- if (a == 100) Left("foo") else Right(a) 
    } yield a 

希望這會有所幫助。