cchantep 和 Marth 都是解决您眼前问题的好方法。但更广泛地说,很难将 Either 视为与Option 完全类似的东西,特别是在让您表达可能失败的推导式计算序列时。要么有一个投影 API(在 cchantep 的解决方案中使用),但它有点坏了。 (两者的投影都通过警卫、模式匹配或变量赋值进行理解。)
FWIW,我写了一个library 来解决这个问题。它用this API 扩充了Either。你为你的任何一个定义一个“偏见”。 “右偏”意味着普通流(map、get 等)由Right 对象表示,而Left 对象表示某种问题。 (右偏是传统的,尽管您也可以根据需要定义左偏。)然后您可以将Either 视为Option;它提供了一个完全类似的 API。
import com.mchange.leftright.BiasedEither
import BiasedEither.RightBias._
val myEither:Either[String, Object] = ...
val o = myEither.getOrElse( "Substitute" )
更有用的是,您现在可以将 Either 视为真正的 scala monad,即使用 flatMap、map、filter 和 for 推导:
val myEither : Either[String, Point] = ???
val nextEither = myEither.map( _.x ) // Either[String,Int]
或
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p )
} yield {
(p, g.population)
}
}
如果所有处理步骤都成功,locPopPair 将是Right[Long]。如果出现任何问题,它将是第一个遇到的Left[String]。
它稍微复杂一些,但定义一个空标记是个好主意。让我们看一下上面 for 理解的细微变化:
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p ) if p.x > 1000
} yield {
(p, g.population)
}
}
如果测试p.x > 1000 失败会怎样?我们想要返回一些表示“空”的Left,但没有普遍适用的值(并非所有Left都是Left[String]。截至目前,代码会抛出一个@ 987654339@。但是我们可以自己指定一个空token,如下:
import com.mchange.leftright.BiasedEither
val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY")
import RightBias._
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p ) if p.x > 1000
} yield {
(p, g.population)
}
}
现在,如果 p.x > 1000 测试失败,则不会出现异常,locPopPair 将只是 Left("EMPTY")。