在scala中使用异常是不好的做法吗?

时间:2012-10-22 13:06:57

标签: scala exception error-handling functional-programming

我已经多次看到使用Option(对于简单值)或者[List [Error],T]来处理错误的scala代码。

这给了像这样的代码

def createApplicationToken(accessToken: AccessToken): Either[List[Error], ApplicationToken] = {

// go to social info provider and fetch information
retrieveProviderInfo(accessToken).fold(
  errors  => Left(errors),
  info    => {
    // try to find user using the info from the provider
    // if it's not there, create user
    User.findOrCreateFromProviderInfo(info).fold(
      errors  => Left(errors),
      user    => {
        // try to create a fresh token and save it to the user
        user.refreshApplicationToken.fold(
          errors  => Left(errors),
          user    => Right(user.token)
        )
      }
    )
  }
)

这会产生一个不那么好的代码嵌套,迫使你处理每一步的失败,并迫使你让所有的函数返回一个[...]

所以我想知道是否

  • 在scala(或一般的函数式编程)中不鼓励使用异常

  • 使用它们有任何缺点(关于不变性或代码并发性)

  • 异常与某些原则或函数式编程相冲突

  • 你可以想出一个更好的方法来编写给定的例子

-

只要使用return语句找到错误就可以通过退出函数来避免嵌套,但是在scala中也不鼓励使用return ...

3 个答案:

答案 0 :(得分:24)

以下版本使用Either的正确投影是monad的事实,并且与您的代码完全等效:

def createApplicationToken(accessToken: AccessToken) = for {
   info <- retrieveProviderInfo(accessToken).right
   user <- User.findOrCreateFromProviderInfo(info).right
   refr <- user.refreshApplicationToken.right
} yield refr.token

在展示Either的优势方面做得更好。

更一般地说,规则与Java中的规则相同:在特殊情况下使用异常。当你以这种方式工作时,你可能会发现你改变了例外的定义 - 例如,无效的用户输入并不是特别的,超时的网络请求并不是真的特殊等等。

自Scala 2.12后右偏Either

您现在可以省略.right,因此以下代码与Scala 2.12相同:

def createApplicationToken(accessToken: AccessToken) = for {
   info <- retrieveProviderInfo(accessToken)
   user <- User.findOrCreateFromProviderInfo(info)
   refr <- user.refreshApplicationToken
} yield refr.token

答案 1 :(得分:4)

正如om-nom-nom所说,我问了一个类似的问题: Throwing exceptions in Scala, what is the "official rule"

但这不是我问过的唯一一个你可能感兴趣的人,因为我曾经用很多样板代码和很多缩进级别编码,因为模式匹配等......


答案 2 :(得分:2)

答案在理想和实际之间有所不同。理想情况下,避免使用异常。实际上,没有它们你就活不下去。

Scala似乎更喜欢单行,并且沿着这些行v2.10有新的monad 尝试

import scala.util.Try

def percentCompleted( total:Int, done:Int ): Int = Try (done * 100 / total) getOrElse 100

percentCompleted( 0, 10 )    // Catches divide-by-zero and returns 100% instead