我想知道我的方法应该具有什么样的签名,以便我可以优雅地处理不同类型的失败。
这个问题有点像我之前在Scala中关于错误处理的许多问题的总结。你可以在这里找到一些问题:
目前我理解以下内容:
- 两者都可以用作可能失败的方法调用的结果包装器
- Try是一个右偏Either,其中失败是非致命异常
- IO(scalaz)有助于构建处理IO操作的纯方法
- 所有3个都可以轻松地在for comprehension中使用
- 由于不兼容的flatMap方法,所有3个在for comprehension中不易混合使用
- 在函数式语言中,我们通常不会抛出异常,除非它们是致命的
- 我们应该为真正异常的情况抛出异常。我想这就是Try的方法
- 创建Throwables对JVM有性能成本,不打算用于业务流程控制
仓库层
现在请考虑我有一个UserRepository
。 UserRepository
存储用户并定义了一个findById
方法。可能会发生以下故障:
- 致命故障(
OutOfMemoryError
) - IO故障,因为数据库无法访问/可读
此外,用户可能丢失,导致Option [User]
结果
使用JDBC实现存储库,可以抛出SQL,非致命异常(约束违规或其他),因此使用Try是有意义的。
由于我们正在处理IO操作,因此如果我们想要纯函数,则IO单子也是有意义的。
因此,结果类型可能是:
Try [Option [User]]
IO [Option [User]]
- 其他?
服务层
现在让我们介绍一个业务层,UserService
,它提供了一些方法updateUserName(id,newUserName)
,该方法使用先前定义的存储库中的findById
。
可能会发生以下故障:
- 所有存储库故障都传播到服务层
- 业务错误:无法更新不存在用户的用户名
- 业务错误:新用户名太短
然后结果类型可以是:
Try[Either[BusinessError,User]]
IO[Either[BusinessError,User]]
- 其他什么?
这里的BusinessError不是Throwable,因为它不是异常故障。
使用for-comprehensions
我希望继续使用for-comprehensions来组合方法调用。
我们不能轻松地在for-comprehension中混合不同的monads,所以我想我应该为所有操作定义一种统一的返回类型,对吗?
我只是想知道,在你们真实的Scala应用程序中,当出现不同类型的故障时,如何成功地保持使用for-comprehensions。
目前,for-comprehension对我来说效果很好,使用服务和存储库,它们都返回Either [Error,Result]
,但是所有不同类型的故障都融合在一起,处理这些故障变得有点繁琐。
您是否定义了不同种类的monads之间的隐式转换以便使用for-comprehensions?
您是否定义了自己的monads来处理故障?
顺便说一下,也许我很快就会使用异步IO驱动程序。因此,我想我的返回类型可能会更加复杂:IO [Future [Either [BusinessError,User]]]
任何建议都将受到欢迎,因为我不知道该使用什么,尽管我的应用程序并不花哨:它只是一个API,我应该能够区分可以显示给客户端的业务错误和技术错误。我试图寻找一种优雅而纯粹的解决方案。