最干净的&使用较小匹配子句处理scala中的自定义错误的最佳方法

时间:2015-03-18 12:17:58

标签: validation scala exception

我试图找到处理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子句。处理自定义错误的最佳方法,在我的情况下,根据你的意愿?

2 个答案:

答案 0 :(得分:0)

你应该切换到Scalaz的\/(我称之为'Scalaz Either'或'或')并将你的'快乐路径'移到右侧。

\/是右侧偏见,允许您在for-understanding中使用它。你可以写:

for {
  resA <- computeA()
  resB <- computeB(resA)
} yield resB

其中computeX返回\/[Error, X]

答案 1 :(得分:0)

在使用for时,

Either理解确实有助于提高可读性/嵌套问题。您可以使用leftright来投射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)
}