左侧的默认值

时间:2013-08-20 08:22:52

标签: scala

我调用外部函数,反过来又有几个Either。说我有

val a = Right("hey")
val b = Right(2)
val c = Left("oops") .....

for{
 x <- a.right
 y <- b.right
 z <- c.right
} yield(User(x,y,z))

但是,如果z是左上方的话。然后我希望给它一个默认的String。即通过

for{
 x <- a.right
 y <- b.right
 z <- c.right.getOrElse(Right("default String")).right
} yield(User(x,y,z))

很脏。我该如何减少这个:c.right.getOrElse(Right("default String")).right。执行c.right.getOrElse("default")将无法作为String上的map返回IndexedSeq。

4 个答案:

答案 0 :(得分:4)

根据this thread

中的建议,可以通过右侧偏置Either 隐式来定义简化语法
val user = {
  implicit def rightBiasEither[A, B](e: Either[A, B]): Either.RightProjection[A,B] =
    e.right

  for {
    x <- a
    y <- b
    z <- c getOrElse (Right("default string"))
  } yield User(x, y, z)
}

您可以使用限制范围选择明确具有右偏置的位置,如上面的示例代码所示,或者将转换包装在自定义对象中以导入将按惯例。

答案 1 :(得分:1)

您可以编写一个函数来执行此操作:

def rightOrElse[A, B](e1: Either[A, B], e2: => Either[A, B]): Either[A, B] =
  e1.right.flatMap(_ => e2)

您仍然需要在结果上调用.right,以便在您的理解中使类型匹配。

使用Scalaz\/(析取)类型(Either的改进版本)更容易实现。它不仅偏向正确,而且无需通过right投影,但它具有更丰富的API,包括orElse方法。

val a = \/.right("hey")
val b = \/.right(2)
val c = \/.left("oops")

for {
  x <- a
  y <- b
  z <- c orElse \/.right("default String")
} yield User(x, y, z)

答案 2 :(得分:0)

另一种方法是使用scala.util.Try。简而言之,Try就像Either一样,都是正确的偏见,并将左侧类型强制为Throwable。如果你的左侧类型可以很容易地映射到Throwable的实例,那么这是一个很好的候选者。

然后您的第一个示例将转换为:

val a = Try("hey")
val b = Try(2)
val c = Try(sys.error("oops"))

for{
 x <- a
 y <- b
 z <- c
} yield(User(x,y,z))

要提供默认值,只需使用orElse

val a = Try("hey")
val b = Try(2)
val c = Try(sys.error("oops"))

for{
 x <- a
 y <- b
 z <- c.orElse(Try("default String"))
} yield(User(x,y,z))

答案 3 :(得分:0)

可能的解决方法是考虑您可以跳过最后一个变量的 for-comprehension

val z = c.fold(Function.const("default string"), identity)

val z = c.right.getOrElse("default string")

然后:

for{
 x <- a.right
 y <- b.right
} yield(User(x,y,z))