【发布时间】:2019-12-30 22:34:45
【问题描述】:
我们的代码库中有一些遗留代码,最终将被重构为使用 Cats 库中的 Validated 和 Either。这是因为 Validated 不使用快速失败机制。未重构的代码使用 Try monad 的快速失败机制。
由于重构尚未发生,我正在做一个笨拙的黑客来解决 Try monad 快速失败的事实。但是我在实现它时遇到了麻烦。
我基本上有一个 Try[T] 类型的列表,它保证都是失败的。
我正在尝试将所有失败的所有错误消息聚合到一个失败中。
这是我正在重构的函数:
private def extractTry[T](xs: IndexedSeq[Try[T]]): Try[IndexedSeq[T]] = {
val failures = xs.collect { case Failure(ex) => Failure(ex) }
if (failures.size > 0) failures.head
else Success(xs.map(_.get))
}
我想聚合所有失败,而不是方法第二行中的 failures.head。
类似
if (failures.size > 0) failures.foldLeft(Failure(new IllegalArgumentException(""))){case (Failure(acc), Failure(e)) => Failure(new IllegalArgumentException(acc.getMessage + e.getMessage))}
我不喜欢这个实现的唯一一点是我希望折叠的每一步都不要使用 IllegalArgumentException,而是使用新元素的异常类型。所以想法是在失败中保持最后一个元素的异常类型,而不是使用任意的异常类型。
我们计划最终使用 Either[Throwable, T] 代替 Try,当我们尝试聚合错误时可能会遇到完全相同的问题。我们希望保留异常类型,而不是像 IllegalArgumentException 那样指定任意类型。所以这个问题迟早要解决,我希望早点解决。
有人有什么建议吗?任何帮助将不胜感激。
【问题讨论】:
-
这真的取决于你想对这些异常做什么?
-
@Luis Miguel Mejía Suárez 我们希望将异常类型保留在Failure 中,因为我们最终将该Failure 传递给Future,完成后将用于完成Akka Http 中的路由。根据异常类型,我们使用不同的错误代码。所以 NoSuchElementException 是 404 而 IllegalArgumentException 是 400。这就是为什么我们需要保留异常类型。
-
在这种情况下,聚合应该如何工作?不能返回多个错误代码。
-
@Luis Miguel Mejía Suárez 这是真的。在聚合期间,我们有一个累加器和一个新元素,结果将具有新元素的异常类型。因此,如果我聚合一连串的失败,它只会保留最后一个失败的异常类型。这只是决定错误代码的启发式方法。当然,如果我们可以向用户显示所有相关的错误代码,我们会很高兴,但是 http 协议当然只允许我们显示一个。
-
我认为从长远来看,最好的办法是创建自己的 ADT 来表示错误,并为此创建自己的 Semigroup。然后,当您重构为 Validate 时,您将完成所有工作。
标签: scala exception functional-programming monads