Scala检查Eithers序列

时间:2016-03-15 15:59:35

标签: scala

我想更新Scala中的序列,我有这段代码:

def update(userId: Long): Either[String, Int] = {
  Logins.findByUserId(userId) map {
    logins: Login => update(login.id,
      Seq(NamedParameter("random_date", "prefix-" + logins.randomDate)))
  } match {
    case sequence : Seq(Nil, Int) => sequence.foldLeft(Right(_) + Right(_))
    case _ => Left("error.logins.update")
  }
}

其中findByUserId返回Seq[Logins]update返回Either[String, Int],其中Int是更新行数, 并且String将是错误的描述。

我想要实现的是,如果在更新列表时发生错误,则返回String,或者使用更新行总数的Int返回。{/ p>

代码无效,我想我应该在match做一些不同的事情,我不知道如何检查Seq Either中的每个元素} s是Right值。

3 个答案:

答案 0 :(得分:2)

如果您愿意使用Scalaz或Cats,则可以使用traverse。使用Scalaz的一个例子:

import scalaz.std.either._
import scalaz.std.list._
import scalaz.syntax.traverse._

val logins = Seq(1, 2, 3)

val updateRight: Int => Either[String, Int] = Right(_)
val updateLeft:  Int => Either[String, Int] = _ => Left("kaboom")

logins.toList.traverseU(updateLeft).map(_.sum)  // Left(kaboom)
logins.toList.traverseU(updateRight).map(_.sum) // Right(6)

遍历登录会为我们提供Either[String, List[Int]],如果我们得到List我们得到想要的Either[String, Int]的总和。

  • 我们使用toList,因为Seq没有Traverse个实例。
  • traversemapsequence的组合。
  • 我们使用traverseU代替traverse,因为它为我们推断了一些类型(否则我们应该引入类型别名或类型lambda)。
  • 由于我们导入了scalaz.std.either._,因此我们可以直接使用map而无需使用正确的投影(.right.map)。

答案 1 :(得分:1)

您希望在更新失败后立即停止,不是吗? 这意味着您希望在map内进行匹配,而不是在外部。 Try实际上是Either用于此目的的更合适的构造。这样的事情,也许是:

def update(userId: Long): Either[String, Int] = Try {
   Logins.findByUserId(userId) map { login => 
     update(login.id, whatever) match {
       case Right(x) => x
       case Left(s) => throw new Exception(s)
     }
   }.sum
}
.map { n => Right(n) }
.recover { case ex => Left(ex.getMessage) }
BTW,关于scala的一个不太广为人知的事实是在lambda中放入一个return语句,实际上是从封闭方法返回的。所以,写另一个更简短的方法就是这样:

def update(userId: Long): Either[String, Int] = 
   Logins.findByUserId(userId).foldLeft(Right(0)) { (sum,login) => 
     update(login.id, whatever) match {
       case Right(x) => Right(sum.right + x)
       case error@Left(s) => return error
     }
   }

另外,为什么世界上findUserById会返回序列 ???

答案 2 :(得分:1)

如果您想提前退出,则不应该使用折叠。更好的解决方案是递归迭代列表,更新和计算成功,然后在遇到错误时返回错误。

这是一个显示技术的小示例函数。您可能希望修改它以在每次登录时执行更新,而不是仅仅计数。

val noErrors = List[Either[String,Int]](Right(10), Right(12))
val hasError = List[Either[String,Int]](Right(10), Left("oops"), Right(12))

def checkList(l: List[Either[String,Int]], goodCount: Int): Either[String, Int] = {
  l match {
    case Left(err) :: xs =>
      Left(err)
    case Right(_) :: xs =>
      checkList(xs, (goodCount + 1))
    case Nil =>
      Right(goodCount)
  }

}

val r1 = checkList(noErrors, 0)
val r2 = checkList(hasError, 0)

// r1: Either[String,Int] = Right(2)
// r2: Either[String,Int] = Left(oops)