“的简单Scala模式”;“使用/尝试资源”;(自动资源管理)

“的简单Scala模式”;“使用/尝试资源”;(自动资源管理),scala,resource-management,Scala,Resource Management,C#具有使用和IDisposable界面。Java 7+具有与try和AutoCloseable接口相同的功能。Scala允许您选择自己的实现来解决这个问题 scala arm似乎是流行的选择,由Typesafe的一名员工维护。然而,对于这样一个简单的行为来说,它似乎非常复杂。澄清一下,使用说明很简单,但是理解所有代码在内部是如何工作的相当复杂 我刚刚编写了以下超级简单的ARM解决方案: object SimpleARM { def apply[T, Q](c: T {def close()

C#具有
使用
IDisposable
界面。Java 7+具有与
try
AutoCloseable
接口相同的功能。Scala允许您选择自己的实现来解决这个问题

scala arm似乎是流行的选择,由Typesafe的一名员工维护。然而,对于这样一个简单的行为来说,它似乎非常复杂。澄清一下,使用说明很简单,但是理解所有代码在内部是如何工作的相当复杂

我刚刚编写了以下超级简单的ARM解决方案:

object SimpleARM {
  def apply[T, Q](c: T {def close(): Unit})(f: (T) => Q): Q = {
    try {
      f(c)
    } finally {
      c.close()
    }
  }
}
  • 简单的手臂有什么好处吗?似乎所有额外的复杂性都应该带来额外的好处
  • 通常,与使用自定义代码相比,最好使用其他人支持的公共、开放源代码库来实现通用行为
  • 有人能推荐一些改进吗
  • 这种简单的方法有什么限制吗

只要您不需要使用多个资源(所有资源都需要管理),您使用单一简单贷款模式的方法就可以正常工作。这是scala arm一元方法允许的

import resource.managed
管理(openResA).和(管理(openResB))获取{(a,b)=>?}
val res=用于{
a


另一个实现,可能比“遵循Java规范”更干净视点,但也无法支持多个资源

这是我较新的简单、一目了然的Scala ARM。它完全支持我能想到的每个用例,包括多个资源和收益值。这使用了一种非常简单的理解用法语法:

class AutoCloseableWrapper[A <: AutoCloseable](protected val c: A) {
  def map[B](f: (A) => B): B = {
    try {
      f(c)
    } finally {
      c.close()
    }
  }

  def foreach(f: (A) => Unit): Unit = map(f)

  // Not a proper flatMap.
  def flatMap[B](f: (A) => B): B = map(f)

  // Hack :)    
  def withFilter(f: (A) => Boolean) = this
}

object Arm {
  def apply[A <: AutoCloseable](c: A) = new AutoCloseableWrapper(c)
}
class AutoCloseableWrapper[ab:B={
试一试{
f(c)
}最后{
c、 关闭()
}
}
def foreach(f:(A)=>单位:单位=地图(f)
//不是一个合适的平面图。
def平面地图[B](f:(A)=>B):B=地图(f)
//黑客:)
def with filter(f:(A)=>布尔)=此
}
物臂{

def apply[A这是我使用的代码:

def use[A <: { def close(): Unit }, B](resource: A)(code: A ⇒ B): B =
    try
        code(resource)
    finally
        resource.close()

这个对我来说真的很好:

  implicit class ManagedCloseable[C <: AutoCloseable](resource: C) {
    def apply[T](block: (C) => T): T = {
    try {
      block(resource)
    } finally {
      resource.close()
    }
  }
甚至更短:

val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)

我可以向您推荐一种改进方法,即:

  def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = {
    try
      code(resource)
    finally
      resource.close()
  }

def autoClose[AChoppy的Lazy TryClose monad可能就是您想要的(披露:我是作者)。它与Scala的Try非常相似,但会自动关闭资源

val ds = new JdbcDataSource()
val output = for {
  conn  <- TryClose(ds.getConnection())
  ps    <- TryClose(conn.prepareStatement("select * from MyTable"))
  rs    <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))

// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
    case Success(result) => // Do something
    case Failure(e) =>      // Handle Stuff
}
val ds=new JdbcDataSource()
val输出=用于{
conn我就是这样做的:

def tryFinally[A,B](获取:Try[A])(使用:A=>B)(释放:A=>Unit):Try[B]=
为了{
资源{release(resource);抛出e},
b=>{release(resource);b}
)
}产量r

你可以调用
.get
,让它返回
B

AFAIK,类型为“c”取决于反射,这在性能方面以及在使用重构或字节码模糊处理时可能会有问题。相反,我只需在此处重用java.lang.AutoCloseable类型。您的代码不处理
c
==null大小写。如果close()也引发了一个异常。因为我需要能够嵌套多个java.lang.AutoCloseable实例,每个实例都取决于前一个成功实例化的实例,所以我最终找到了一个对我非常有用的模式。我将其作为类似StackOverflow问题的答案:StackOverflow.com/a/34277491/501113@chaotic3quilibri嗯,我下面的答案包含一个超级简单的Arm系统,它支持您描述的嵌套类型。哎呀。这里有一个指向我的答案的可点击链接(关于一个类似的和相关的问题):Arm在上也提到过,但尽管这个项目有了一些新的活力,但它是否足够可靠,可以包含在一个项目中还不清楚。这么多年来“孵化器"状态而不是合并到RTL中…对于理解来说,有一个特别重要的bug:您的解决方案隐式依赖于反射。是的,您使用的duck类型只能通过反射来使用。因此,这意味着您的解决方案无法推广到Scala.js或ScalaNative这样的地方。我认为它不使用运行时反射。现在是时候更新一下您的想法了:这里有更多关于结构类型的内容:您的解决方案太像OOP黑客了,特别是AutoCloseableWrapper。我提供的解决方案更符合Scala的基本目标FP和不变性,同时保留了Java ARM mod的核心优势el all在具有两个(当前)参数列表的单个函数中:
val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)
  def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = {
    try
      code(resource)
    finally
      resource.close()
  }
  def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): Try[B] = {
    val tryResult = Try {code(resource)}
    resource.close()
    tryResult
  }
val ds = new JdbcDataSource()
val output = for {
  conn  <- TryClose(ds.getConnection())
  ps    <- TryClose(conn.prepareStatement("select * from MyTable"))
  rs    <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))

// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
    case Success(result) => // Do something
    case Failure(e) =>      // Handle Stuff
}