安全尝试清理

时间:2018-08-01 06:44:09

标签: scala monads scalaz

我一直在尝试找到一种很好的方法,将Scala中的Try链与后续资源清理结合起来。我最终看到的是这样的(由scalaz提供支持)

for {
  x <- \/.fromTryCatchNonFatal(...)
  y <- \/.fromTryCatchNonFatal(...).leftMap( /* clean up x */ )
  z <- \/.fromTryCatchNonFatal(...).leftMap( /* clean up x and y */ )
  // clean up everything
} yield (...)

这种方法的问题是,如果您有3个以上的Try块,可能会发生某些情况下的清理工作。更不用说它看起来笨重和丑陋。

所以我想出了一个Scala的包装器,尝试使用scalaz提供的State和Either单子。

我最终看到的是这样的:

val try = for {
  a <- Try(/* no resources allocated here */).noRes
  c <- Try(20).res { x => /** create DB connection */ }.res(/* release DB connection */)
} yield (..)

val result: Throwable \/ T = try.safeEval

通过这种方式从您的工作中获得的好处是,您可以确保在发生异常情况和成功计算的情况下都将释放所有资源。

Full code is here on GitHub

此代码尚未准备好用于生产,肯定需要一些改进。

您如何看待,您认为这种方法可行吗? 您会建议以不同的方式做同一件事吗?

1 个答案:

答案 0 :(得分:2)

这正是Bracket的用途。它以cats-effect定义,是MonadError的扩展,可为您提供安全的资源处理。例如:

val foo = 
  IO.pure(42).bracket(int => /* use resource */ ???)(_ => /* release resource */ ???)