为什么Scala的Either.RightProjection#filter没有返回Either?

ms-*_*-tg 6 functional-programming scala

使用Scala 2.9.1,考虑以下两个实例Either:

scala> val er: Either[String, Int] = Right(1)
er: Either[String,Int] = Right(1)

scala> val el: Either[String, Int] = Left("a")
el: Either[String,Int] = Left(a)
Run Code Online (Sandbox Code Playgroud)

很棒的是,通过使用leftright投影,人们可以使用for-comprehension(通过投射Either的偏向monad):

scala> for { r <- er.right } yield r * 2
res6: Product with Either[String,Int] with Serializable = Right(2)

scala> for { r <- el.right } yield r * 2
res7: Product with Either[String,Int] with Serializable = Left(a)
Run Code Online (Sandbox Code Playgroud)

有人可以向我解释为什么决定不让filter方法返回Either?我希望以下工作:

scala> for { r <- er.right if r > 2 } yield r * 2 // r is NOT greater than 2!
res8: Product with Either[String,Int] with Serializable = Left(a)
Run Code Online (Sandbox Code Playgroud)

相反,您会收到以下错误:: 9:错误:值*不是{r < - er.right的[Nothing,Int]的成员,如果r> 2},则产生r*2

看来底层调用Either.RightProjection#filter实际上返回一个Option:

scala> er.right.filter(_ > 2)
res9: Option[Either[Nothing,Int]] = None
Run Code Online (Sandbox Code Playgroud)

这使得在for-comprehension中使用if子句失败了,至少我尝试使用它的方式.

有没有人解释为什么这种设计是这样的?

Ben*_*mes 12

它归结为这样一个事实:如果你有一个Right(b),但你的过滤谓词失败了,你就没有价值Left.

您可以想象一个适用于您的情况的实现Either[String, Int],通过使用默认值失败Left("").Scala标准库没有为您生成值的功能,因为它不包含诸如monoid之类的概念,它将确定类型的"空"值.

Scalaz库确实包含一个monoid类型类,而且版本7还包括一个右偏的取类型,\/[A, B](同构Either[A, B]),filter如果左类型是一个monoid ,它有一个方法:

scala> \/.right[String, Int](1).filter(_ > 2)
res1: scalaz.\/[String,Int] = -\/()
Run Code Online (Sandbox Code Playgroud)

但是对于一般情况你不能这样做 - 如果你有Either[Nothing, Int],你永远不会产生左值.