我试图找到处理scala中自定义错误的最佳和最干净的方法。
目前我有一个常见的全局错误对象(我的代码与此类似):
sealed trait AppError {
def message: String
}
object AppError {
/* Cat Error Messages */
case class CatNotFound(id: Long) extends AppError {
val message = Messages("cat.not_found").format(id)
}
case class CatNotForUser(id: Long) extends AppError {
val message = Messages("cat.not_for_user").format(id)
}
}
每个方法都返回Either[<SomeValidType>, AppError]
(我在右侧发送错误),例如:
def getCatForUser(id: Long, user: User): Either[Boolean, AppError] = {
CatService get(id) match {
case None => CatNotFound(id)
case Some(x) =>
CatService isAccessibleByUser(x, user) match {
case false => CatNotForUser(id)
case true => true
}
}
}
调用验证/获取部分的方法有很多嵌套match
,如下所示:
def something(id: Long, user: User) = {
getCatForUser(id, user) match {
case Left(b) =>
anotherMethodReturnsEither(id) match {
case Left(x) =>
thereCanBeLotOfNesting(user) match {
case Left(y) =>
// do something or create another nesting call
case Right(e) =>
handleSomeHow(e)
}
case Right(e) =>
handleSomeHow(e)
}
case Right(e) =>
handleSomeHow(e)
}
}
def handleSomeHow(e: AppError) = {
e match {
case CatNotFound(_) => NotFound(e.message)
case CatNotForUser(_) => Unauthorized(e.message)
}
}
在一个项目中,我使用CustomExceptions
来处理这样的嵌套/ if else。在那种情况下,我抛出的错误传播到顶部,可以一起处理。 当我们使用异常而不是错误时,代码看起来很干净。但是我认为在验证数据时出现异常问题&amp;有一个这样的方法除了throw Exception
之外不会返回任何内容:
def validateCatForUser(id: Long, user: User) = {
CatService get(id) match {
case None => throw CatNotFound(id)
case Some(x) =>
CatService isAccessibleByUser(x, user) match {
case false => throw CatNotForUser(id)
case true => // nothing can be returned as its validation
}
}
}
我将使用它:
def something(id: Long, user: User) = {
validateCatForUser(id, user)
/*
* here will be some logic after validation
*/
}
仍然干净,可读。所以我的问题是我应该使用什么来实现更易读的代码,match
子句更少Left() or Right()
。我正在阅读有关scala验证的内容,并找到了http://blog.lunatech.com/2012/03/02/validation-scala但是scala&#39;验证&#39;还会返回ValidationNEL[String, <some valid type>]
,它不会减少match
子句。处理自定义错误的最佳方法,在我的情况下,根据你的意愿?
答案 0 :(得分:0)
你应该切换到Scalaz的\/
(我称之为'Scalaz Either'或'或')并将你的'快乐路径'移到右侧。
\/
是右侧偏见,允许您在for-understanding中使用它。你可以写:
for {
resA <- computeA()
resB <- computeB(resA)
} yield resB
其中computeX
返回\/[Error, X]
。
答案 1 :(得分:0)
for
时, Either
理解确实有助于提高可读性/嵌套问题。您可以使用left
和right
来投射Eithers
并生成将收集错误的Either
:
val x = Left(5) : Either[Int, String]
val y = Right("error") : Either[Int, String]
val z = Right("error") : Either[Int, String]
for {
xval <- x.left
yval <- y.left
zval <- z.left
} yield zval
使用此语法可以使您的嵌套版本更清晰。它可能看起来像这样:
def something(id: Long, user: User) = {
for {
cat <- getCatForUser(id, user).left
otherValues <- anotherMethodReturnsEither(id).left
noNesting <- thereCanBeLotOfNesting(user).left
} yield noNesting
} match {
case Left(value) => // do something
case Right(e) => handleSomeHow(e)
}