如何确保在Scala {for}中理解资源

时间:2015-09-02 10:45:38

标签: scala

如何在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但代码不是很好。 是否有更聪明,更干净的方法来实现同样的目标?

3 个答案:

答案 0 :(得分:6)

当我遇到这样的问题时,我决定两种可能性:

  1. 使用Scala ARM
  2. 自己实施Loan Pattern(链接易失,可能会死)
  3. 在大多数情况下,我更喜欢自己的实现,以避免额外的依赖。 这是贷款模式的代码:

    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) }