Scala有哪些自动资源pipe理替代scheme?

我在网上看到了很多关于Scala的ARM(自动资源pipe理)的例子。 写一篇文章似乎是一种仪式,尽pipe大部分看起来都很像。 不过,我确实看到了一个非常酷的使用延续的例子。

无论如何,很多代码都有这种或那种types的缺陷,所以我认为在Stack Overflow上有一个参考是个好主意,在这里我们可以投票选出最正确和最合适的版本。

丹尼尔,

我刚刚部署了自动资源pipe理的scala-arm库。 你可以在这里find文档: http : //wiki.github.com/jsuereth/scala-arm/

这个库支持三种使用方式(当前):

1)命令/expression式:

import resource._ for(input <- managed(new FileInputStream("test.txt")) { // Code that uses the input as a FileInputStream } 

2)Monadic风格

 import resource._ import java.io._ val lines = for { input <- managed(new FileInputStream("test.txt")) val bufferedReader = new BufferedReader(new InputStreamReader(input)) line <- makeBufferedReaderLineIterator(bufferedReader) } yield line.trim() lines foreach println 

3)划定延续式

这是一个“回声”TCP服务器:

 import java.io._ import util.continuations._ import resource._ def each_line_from(r : BufferedReader) : String @suspendable = shift { k => var line = r.readLine while(line != null) { k(line) line = r.readLine } } reset { val server = managed(new ServerSocket(8007)) ! while(true) { // This reset is not needed, however the below denotes a "flow" of execution that can be deferred. // One can envision an asynchronous execuction model that would support the exact same semantics as below. reset { val connection = managed(server.accept) ! val output = managed(connection.getOutputStream) ! val input = managed(connection.getInputStream) ! val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(output))) val reader = new BufferedReader(new InputStreamReader(input)) writer.println(each_line_from(reader)) writer.flush() } } } 

该代码使用了资源types特征,因此它能够适应大多数资源types。 它有一个使用close或dispose方法对类进行结构types化的后备方法。 请查看文档,并让我知道如果你想添加任何方便的function。

Chris Hansen的博客文章“Scala中的ARM Blocks:Revisited”从3/26/09谈到了Martin Odersky的FOSDEM演示文稿幻灯片21。 下一个区块从第21张(获得许可)直接获取:

 def using[T <: { def close() }] (resource: T) (block: T => Unit) { try { block(resource) } finally { if (resource != null) resource.close() } } 

– 结束引用

那我们可以这样打电话:

 using(new BufferedReader(new FileReader("file"))) { r => var count = 0 while (r.readLine != null) count += 1 println(count) } 

这种方法有什么缺点? 这种模式似乎解决了95%的需要自动资源pipe理的地方…

编辑:添加代码片段


Edit2:扩展devise模式 – 从python中获取灵感with声明和解决:

  • 语句在块之前运行
  • 根据托pipe资源重新抛出exception
  • 用一个使用语句处理两个资源
  • 通过提供隐式转换和Managed类来处理特定于资源的处理

这是与斯卡拉2.8。

 trait Managed[T] { def onEnter(): T def onExit(t:Throwable = null): Unit def attempt(block: => Unit): Unit = { try { block } finally {} } } def using[T <: Any](managed: Managed[T])(block: T => Unit) { val resource = managed.onEnter() var exception = false try { block(resource) } catch { case t:Throwable => exception = true; managed.onExit(t) } finally { if (!exception) managed.onExit() } } def using[T <: Any, U <: Any] (managed1: Managed[T], managed2: Managed[U]) (block: T => U => Unit) { using[T](managed1) { r => using[U](managed2) { s => block(r)(s) } } } class ManagedOS(out:OutputStream) extends Managed[OutputStream] { def onEnter(): OutputStream = out def onExit(t:Throwable = null): Unit = { attempt(out.close()) if (t != null) throw t } } class ManagedIS(in:InputStream) extends Managed[InputStream] { def onEnter(): InputStream = in def onExit(t:Throwable = null): Unit = { attempt(in.close()) if (t != null) throw t } } implicit def os2managed(out:OutputStream): Managed[OutputStream] = { return new ManagedOS(out) } implicit def is2managed(in:InputStream): Managed[InputStream] = { return new ManagedIS(in) } def main(args:Array[String]): Unit = { using(new FileInputStream("foo.txt"), new FileOutputStream("bar.txt")) { in => out => Iterator continually { in.read() } takeWhile( _ != -1) foreach { out.write(_) } } } 

James Iry解决scheme使用continuation:

 // standard using block definition def using[X <: {def close()}, A](resource : X)(f : X => A) = { try { f(resource) } finally { resource.close() } } // A DC version of 'using' def resource[X <: {def close()}, B](res : X) = shift(using[X, B](res)) // some sugar for reset def withResources[A, C](x : => A @cps[A, C]) = reset{x} 

以下是有和没有延续比较的解决scheme:

 def copyFileCPS = using(new BufferedReader(new FileReader("test.txt"))) { reader => { using(new BufferedWriter(new FileWriter("test_copy.txt"))) { writer => { var line = reader.readLine var count = 0 while (line != null) { count += 1 writer.write(line) writer.newLine line = reader.readLine } count } } } } def copyFileDC = withResources { val reader = resource[BufferedReader,Int](new BufferedReader(new FileReader("test.txt"))) val writer = resource[BufferedWriter,Int](new BufferedWriter(new FileWriter("test_copy.txt"))) var line = reader.readLine var count = 0 while(line != null) { count += 1 writer write line writer.newLine line = reader.readLine } count } 

以下是Tiark Rompf的改进build议:

 trait ContextType[B] def forceContextType[B]: ContextType[B] = null // A DC version of 'using' def resource[X <: {def close()}, B: ContextType](res : X): X @cps[B,B] = shift(using[X, B](res)) // some sugar for reset def withResources[A](x : => A @cps[A, A]) = reset{x} // and now use our new lib def copyFileDC = withResources { implicit val _ = forceContextType[Int] val reader = resource(new BufferedReader(new FileReader("test.txt"))) val writer = resource(new BufferedWriter(new FileWriter("test_copy.txt"))) var line = reader.readLine var count = 0 while(line != null) { count += 1 writer write line writer.newLine line = reader.readLine } count } 

丹尼尔,你问这个问题。 看到James Iry的代码后,我自己也很感兴趣。 我在Scala中看到了一个循序渐进的4步骤演变:

  1. 没有ARM:污垢
  2. 只有closures:更好,但多个嵌套块
  3. 继续Monad:用于平铺嵌套,但在2块不自然的分离
  4. 直接风格延续:Nirava,哈哈! 这也是最安全的select:资源块以外的资源将是types错误。

我真的很想看到的是一个介绍这些的演示文稿。 这将是非常有教育意义的,并应该说服乞讨人们有一个世界以外的Monads 🙂

有较轻的(10行代码)ARM包含更好的文件。 请参阅: https : //github.com/pathikrit/better-files#lightweight-arm

 import better.files._ for { in <- inputStream.autoClosed out <- outputStream.autoClosed } in.pipeTo(out) // The input and output streams are auto-closed once out of scope 

如果你不想要整个图书馆,这是如何实现的:

  type Closeable = { def close(): Unit } type ManagedResource[A <: Closeable] = Traversable[A] implicit class CloseableOps[A <: Closeable](resource: A) { def autoClosed: ManagedResource[A] = new Traversable[A] { override def foreach[U](f: A => U) = try { f(resource) } finally { resource.close() } } } 

如何使用Type类

 trait GenericDisposable[-T] { def dispose(v:T):Unit } ... def using[T,U](r:T)(block:T => U)(implicit disp:GenericDisposable[T]):U = try { block(r) } finally { Option(r).foreach { r => disp.dispose(r) } }