当函数具有参数前提条件时,返回Either / Option / Try / Or是否被视为可行/惯用的方法?

Gap*_*rei 2 error-handling scala

首先,我是Scala的新手,没有任何编写生产代码的经验,因此我缺乏对社区中被认为是好的/最佳实践的理解。我偶然发现了以下资源:

  1. https://github.com/alexandru/scala-best-practices
  2. https://nrinaudo.github.io/scala-best-practices/

在那里提到抛出异常不是很好的实践,这使我认为那时定义函数前提条件的好方法是什么,因为

抛出的函数有点谎言:它的类型暗含着它的全部功能。

一些研究之后,似乎使用Option/ Either/ Try/ Or(scalactic)是一个更好的办法,因为你可以使用类似T Or IllegalArgumentException的返回类型明确指出该功能实际上是局部的,使用异常的方式来存储信息,即可以包装在其他异常中。

但是缺乏Scala经验,我不太了解这对于实际项目是可行的方法还是可行的方法Predef.require。如果有人解释了Scala社区通常的工作方式以及原因,我将不胜感激。

我也曾在Scala中看到过功能断言,但是尽管这个想法本身看起来很有趣,但我认为PartialFunction它并不是很适合此目的,因为在这种情况下,经常会传递多个参数,并且元组看起来像是hack。

Mar*_*pel 6

OptionEither绝对是进行函数式编程的方式。

随着Option它是重要的文件为什么 None会被退回。

使用Either,左侧是不成功的值(“错误”),而右侧是成功的值。左侧不必一定是它的Exception(或它的子类型),它可以是一条简单的错误消息String(类型别名是您的朋友)或适合您应用程序的自定义数据类型。

例如,在处理错误时,我通常使用以下模式Either

// Somewhere in a package.scala
type Error = String // Or choose something more advanced
type EitherE[T] = Either[Error, T]

// Somewhere in the program
def fooMaybe(...): EitherE[Foo] = ...
Run Code Online (Sandbox Code Playgroud)

Try应该只用于包装不安全的(大多数情况下,纯Java)代码,使您能够对结果进行模式匹配:

Try(fooDangerous()) match {
   case Success(value) => ...
   case Failure(value) => ...
}
Run Code Online (Sandbox Code Playgroud)

但是我建议仅Try在本地使用,然后从那里使用上述数据类型。

一些高级数据类型喜欢本机cats.effect.IOmonix.reactive.Observable包含错误处理。

我也建议研究cats.data.EitherT基于类型类的错误处理。阅读文档,绝对值得。


附带说明一下,对于来自Java的每个人,Scala都将Exceptions视为Java对待RuntimeException。这意味着,即使您的一个依赖项中的不安全代码抛出了(已检查)IOException,Scala也将永远不会要求您catch或以其他方式处理该异常。因此,根据经验,使用Java依赖项时,几乎总是将它们包装在Try(或IO执行副作用或阻塞线程的情况下)中。


yǝs*_*ǝla 5

我认为您的推理是正确的。如果您有一个简单的total(部分函数的对立)函数,且参数可能具有无效类型,那么最常见和最简单的解决方案是返回一些可选结果,例如Option,等等。

通常不建议抛出异常,因为它们会违反FP法律。如果您需要以笨拙的方式Option编写Validation结果,则可以使用任何可以返回比Scalaz 更为高级的类型的库Option

我可以提供的另外两种替代方法是:

  1. 输入实施前提条件的受约束参数。示例:val i: Int Refined Positive = 5基于https://github.com/fthomas/refined。您也可以编写自己的类型,这些类型可以包装原始类型并声明一些属性。这里的问题是,如果您的参数具有多个相互依赖的有效值,则每个参数互斥。例如x > 1 and y < 1x < 1 and y > 1。在这种情况下,您可以返回一个可选值,而不是使用此方法。
  2. 局部函数,本质上类似于可选的返回类型:case i: Int if i > 0 => ...。文件:https : //www.scala-lang.org/api/2.12.1/scala/PartialFunction.html

例如:PF def lift: (A) ? Option[B]将PF转换为常规函数。

将此部分函数转换为简单函数,返回Option结果。

这类似于返回选项。部分功能的问题是它们使用起来有点笨拙,并且不完全与FP友好。

我认为这Predef.require属于非常罕见的情况,在这种情况下,您不想允许构造任何无效数据,并且更多地是在这种情况下制止一切。例如,您将获得从未获得的参数。