Scala 向右映射或 return 向左映射

Scala Either map Right or return Left

是否可以像 Option 一样处理 Either?在 Option 中,我有一个 getOrElse 函数,在 Either 中,我想 return Left 或处理 Right。我正在寻找没有任何样板的最快方法,例如:

val myEither:Either[String, Object] = Right(new Object())
myEither match {
    case Left(leftValue) => value
    case Right(righValue) => 
        "Success"
}

我想你可以按照下面的方式做。

def foo(myEither: Either[String, Object]) = 
  myEither.right.map(rightValue => "Success")

您可以使用 .fold:

scala> val r: Either[Int, String] = Right("hello")
r: Either[Int,String] = Right(hello)

scala> r.fold(_ => "got a left", _ => "Success")
res7: String = Success

scala> val l: Either[Int, String] = Left(1)
l: Either[Int,String] = Left(1)

scala> l.fold(_ => "got a left", _ => "Success")
res8: String = got a left

编辑:

Re-reading 你的问题我不清楚你是想 return Left 中的值还是另一个(在别处定义的)
如果是前者,您可以将 identity 传递给 .fold,但这可能会将 return 类型更改为 Any:

scala> r.fold(identity, _ => "Success")
res9: Any = Success

cchantep 和 Marth 都是解决您当前问题的好方法。但更广泛地说,很难将 Either 视为与 Option 完全类似的东西,尤其是在让您表达可能会失败的计算序列以供理解时。要么有一个投影API(在cchantep的解决方案中使用),但它有点坏了。 (其中任何一个的预测都会因守卫、模式匹配或变量赋值的理解而中断。)

FWIW,我写了一篇 library to solve this problem. It augments Either with this API。您为您的 Eithers 定义了一个“偏见”。 “右偏”意味着普通流(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 和理解:

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 失败会怎样?我们想要 return 一些 Left 表示“空”,但没有普遍适用的值(并非所有 Left 都是 Left[String]。截至目前,会发生的是代码会抛出一个 NoSuchElementException。但是我们可以自己指定一个空标记,如下所示:

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")

在 Scala 2.12 中,

Either is right-biased, which means that Right is assumed to be the default case to operate on. If it is Left, operations like map, flatMap, ... return the Left value unchanged

所以你可以做到

myEither.map(_ => "Success").merge

如果您觉得它比 fold 更具可读性。

在 scala 2.13 中,您可以使用 myEither.getOrElse

   Right(12).getOrElse(17) // 12
   Left(12).getOrElse(17)  // 17