我调用外部函数,反过来又有几个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。
答案 0 :(得分:4)
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))