我已经多次看到使用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 ...
答案 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中的规则相同:在特殊情况下使用异常。当你以这种方式工作时,你可能会发现你改变了例外的定义 - 例如,无效的用户输入并不是特别的,超时的网络请求并不是真的特殊等等。
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"
但这不是我问过的唯一一个你可能感兴趣的人,因为我曾经用很多样板代码和很多缩进级别编码,因为模式匹配等......
您可以查看以下链接: Handling failures with Either -> Where is the stacktrace?
与错误处理有关的种类,您可能会感兴趣: Method parameters validation in Scala, with for comprehension and monads Travis Brown给出了一个更详细的答案,关于使用applicative functor和Scalaz做快失败(第一次错误阻止过程)或收集一系列操作的所有错误 同样的问题:Handling fail-fast failures when the return type is Option[Error]
你可以检查这个使用by-name参数的链接是否也执行了一系列操作。它可能是在理解中使用正确投影的一个很好的替代方法,但你不能创建intermediare结果:( Validation with a sequence of by-name parameters in Scala? 我不知道它是否可以与您的代码示例一起使用但在这种情况下我不这么认为(假设您的refreshApplicationToken没有副作用并返回新创建的不可变用户实例,而不是改变令牌变量)< / p>
答案 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