如何在Scala中的for -reherehest中最好地处理带有副作用的函数?
我有一个理解,首先是通过调用函数 f1 来创建一种资源( x )。该资源有一个 close - 方法,需要在结束时调用,但如果for-comprehension以某种方式失败(除非。
所以我们有类似的东西:
import scala.util.{Try,Success,Failure}
trait Resource {
def close() : Unit
}
// Opens some resource and returns it as Success or returns Failure
def f1 : Try[Resource] = ...
def f2 : Try[Resource] = ...
val res = for {
x <- f1
y <- f2
} yield {
(x,y)
}
我应该在哪里调用close方法?我可以在for-comprehension结束时将其称为最后一个语句(z&lt; - x.close),在yield-part中,或在for-comprehension之后(res._1.close)。如果发生错误(例如,如果 f2 失败),它们都不会确保调用关闭。 或者,我可以分开
x <- f1
出于这样的理解:
val res = f1
res match {
case Success(x) => {
for {
y <- f2
}
x.close
}
case Failure(e) => ...
:
这将确保调用close但代码不是很好。 是否有更聪明,更干净的方法来实现同样的目标?
答案 0 :(得分:6)
当我遇到这样的问题时,我决定两种可能性:
在大多数情况下,我更喜欢自己的实现,以避免额外的依赖。 这是贷款模式的代码:
def using[A](r : Resource)(f : Resource => A) : A =
try {
f(r)
} finally {
r.close()
}
用法:
using(getResource())(r =>
useResource(r)
)
由于您需要2个资源,因此需要使用此模式两次:
using(getResource1())(r1 =>
using(getResource2())(r2 =>
doYourWork(r1, r2)))
您还可以查看以下答案:
答案 1 :(得分:3)
关闭资源的常见模式是贷款模式:
type Closable = { def close(): Unit }
def withClosable[B](closable: Closable)(op: Closable => B): B = {
try {
op(closable)
} finally {
closable.close()
}
}
通过一些重构,您可以使用此模式:
import scala.util.{Try,Success,Failure}
trait Resource {
def close() : Unit
}
// Opens some resource and returns it as Success or returns Failure
def f1(res: Resource) : Try[Resource] = ???
def f2(res: Resource) : Try[Resource] = ???
val f1Resource: Resource = ???
val f2Resource: Resource = ???
val res = for {
x <- withClosable(f1Resource)(f1)
y <- withClosable(f2Resource)(f2)
} yield {
(x,y)
}
或
import scala.util.{Try,Success,Failure}
trait Resource {
def close() : Unit
}
// Opens some resource and returns it as Success or returns Failure
def f1: Try[Resource] = {
val res: Resource = ???
withClosable(res){ ... }
}
def f2: Try[Resource] = {
val res: Resource = ???
withClosable(res){ ... }
}
val res = for {
x <- f1
y <- f2
} yield {
(x,y)
}
答案 2 :(得分:1)
您可以使用
https://github.com/jsuereth/scala-arm
如果你的“资源”没有实现java.io.Closeable(或者是一些其他可关闭的接口,比库支持的那样),你只需要写一个隐式转换:
implicit def yourEnititySupport[A <: your.closable.Enitity]: Resource[A] =
new Resource[A] {
override def close(r: A) = r.commit()
// if you need custom behavior here
override def closeAfterException(r: A, t: Throwable) = r.rollback()
}
并像这样使用它:
import resource._
for {
a <- managed(your.closable.Enitity())
b <- managed(your.closable.Enitity())
} { doSomething(a, b) }