我想更新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
值。
答案 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
个实例。traverse
是map
和sequence
的组合。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)