Scala 有哪些自动资源管理替代方案?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2207425/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What Automatic Resource Management alternatives exist for Scala?
提问by Daniel C. Sobral
I have seen many examples of ARM (automatic resource management) on the web for Scala. It seems to be a rite-of-passage to write one, though most look pretty much like one another. I didsee a pretty cool example using continuations, though.
我在网上看到了很多关于 Scala 的 ARM(自动资源管理)的例子。写一个似乎是一种仪式,尽管大多数看起来都非常相似。不过,我确实看到了一个使用延续的非常酷的例子。
At any rate, a lot of that code has flaws of one type or another, so I figured it would be a good idea to have a reference here on Stack Overflow, where we can vote up the most correct and appropriate versions.
无论如何,很多代码都有一种或另一种类型的缺陷,所以我认为在 Stack Overflow 上有一个参考是个好主意,我们可以在那里投票选出最正确和最合适的版本。
采纳答案by chengpohi
For now Scala 2.13has finally supported: try with resourcesby using Using:), Example:
现在Scala 2.13终于支持了:try with resources通过使用Using:),示例:
val lines: Try[Seq[String]] =
Using(new BufferedReader(new FileReader("file.txt"))) { reader =>
Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
}
or using Using.resourceavoid Try
或使用Using.resource避免Try
val lines: Seq[String] =
Using.resource(new BufferedReader(new FileReader("file.txt"))) { reader =>
Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
}
You can find more examples from Usingdoc.
您可以从Usingdoc 中找到更多示例。
A utility for performing automatic resource management. It can be used to perform an operation using resources, after which it releases the resources in reverse order of their creation.
用于执行自动资源管理的实用程序。它可用于使用资源执行操作,然后以与创建相反的顺序释放资源。
回答by huynhjl
Chris Hansen's blog entry 'ARM Blocks in Scala: Revisited' from 3/26/09talks about about slide 21 of Martin Odersky's FOSDEM presentation. This next block is taken straight from slide 21 (with permission):
2009 年 3 月 26 日 Chris Hansen 的博客文章“Scala 中的 ARM 块:重新访问”讨论了 Martin Odersky 的FOSDEM 演示文稿的第21 张幻灯片。下一个块直接取自幻灯片 21(经许可):
def using[T <: { def close() }]
(resource: T)
(block: T => Unit)
{
try {
block(resource)
} finally {
if (resource != null) resource.close()
}
}
--end quote--
--结束报价--
Then we can call like this:
然后我们可以这样调用:
using(new BufferedReader(new FileReader("file"))) { r =>
var count = 0
while (r.readLine != null) count += 1
println(count)
}
What are the drawbacks of this approach? That pattern would seem to address 95% of where I would need automatic resource management...
这种方法的缺点是什么?这种模式似乎可以解决我需要自动资源管理的 95% 的问题......
Edit:added code snippet
编辑:添加了代码片段
Edit2:extending the design pattern - taking inspiration from python withstatement and addressing:
Edit2:扩展设计模式——从pythonwith语句中汲取灵感并解决:
- statements to run before the block
- re-throwing exception depending on the managed resource
- handling two resources with one single using statement
- resource-specific handling by providing an implicit conversion and a
Managedclass
- 在块之前运行的语句
- 根据托管资源重新抛出异常
- 用一个 using 语句处理两个资源
- 通过提供隐式转换和
Managed类进行特定于资源的处理
This is with Scala 2.8.
这是 Scala 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(_)
}
}
}
回答by jsuereth
Daniel,
丹尼尔,
I've just recently deployed the scala-arm library for automatic resource management. You can find the documentation here: https://github.com/jsuereth/scala-arm/wiki
我最近刚刚部署了用于自动资源管理的 scala-arm 库。您可以在此处找到文档:https: //github.com/jsuereth/scala-arm/wiki
This library supports three styles of usage (currently):
这个库支持三种使用风格(目前):
1) Imperative/for-expression:
1) 命令式/表达式:
import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}
2) Monadic-style
2) 一元式
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) Delimited Continuations-style
3) 带分隔符的 Continuations 样式
Here's an "echo" tcp server:
这是一个“echo”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()
}
}
}
The code makes uses of a Resource type-trait, so it's able to adapt to most resource types. It has a fallback to use structural typing against classes with either a close or dispose method. Please check out the documentation and let me know if you think of any handy features to add.
该代码使用了 Resource 类型特征,因此它能够适应大多数资源类型。它有一个回退,可以使用 close 或 dispose 方法对类使用结构化类型。请查看文档,如果您想添加任何方便的功能,请告诉我。
回答by Daniel C. Sobral
Here's James Irysolution using continuations:
这是使用延续的James Iry解决方案:
// 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}
Here are the solutions with and without continuations for comparison:
以下是有和没有延续的解决方案进行比较:
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
}
And here's Tiark Rompf's suggestion of improvement:
这是 Tiark Rompf 的改进建议:
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
}
回答by Mushtaq Ahmed
I see a gradual 4 step evolution for doing ARM in Scala:
我看到在 Scala 中执行 ARM 的渐进式 4 步演变:
- No ARM: Dirt
- Only closures: Better, but multiple nested blocks
- Continuation Monad: Use For to flatten the nesting, but unnatural separation in 2 blocks
- Direct style continuations: Nirava, aha! This is also the most type-safe alternative: a resource outside withResource block will be type error.
- 无臂:污垢
- 只有闭包:更好,但有多个嵌套块
- Continuation Monad:使用 For 使嵌套变平,但在 2 个块中不自然的分离
- 直接风格延续:Nirava,啊哈!这也是最类型安全的替代方案:在 withResource 块之外的资源将是类型错误。
回答by pathikrit
There is light-weight (10 lines of code) ARM included with better-files. See: https://github.com/pathikrit/better-files#lightweight-arm
Better-files 包含轻量级(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
Here is how it is implemented if you don't want the whole library:
如果您不想要整个库,以下是它的实现方式:
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()
}
}
}
回答by Santhosh Sath
How about using Type classes
如何使用 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) }
}
回答by ChoppyTheLumberHyman
Another alternative is Choppy's Lazy TryClose monad. It's pretty good with database connections:
另一种选择是 Choppy 的 Lazy TryClose monad。数据库连接非常好:
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
}
And with streams:
并使用流:
val output = for {
outputStream <- TryClose(new ByteArrayOutputStream())
gzipOutputStream <- TryClose(new GZIPOutputStream(outputStream))
_ <- TryClose.wrap(gzipOutputStream.write(content))
} yield wrap({gzipOutputStream.flush(); outputStream.toByteArray})
output.resolve.unwrap match {
case Success(bytes) => // process result
case Failure(e) => // handle exception
}
More info here: https://github.com/choppythelumberHyman/tryclose
回答by Mike Slinn
Here is @chengpohi's answer, modified so it works with Scala 2.8+, instead of just Scala 2.13 (yes, it works with Scala 2.13 also):
这是@chengpohi 的答案,经过修改,使其适用于 Scala 2.8+,而不仅仅是 Scala 2.13(是的,它也适用于 Scala 2.13):
def unfold[A, S](start: S)(op: S => Option[(A, S)]): List[A] =
Iterator
.iterate(op(start))(_.flatMap{ case (_, s) => op(s) })
.map(_.map(_._1))
.takeWhile(_.isDefined)
.flatten
.toList
def using[A <: AutoCloseable, B](resource: A)
(block: A => B): B =
try block(resource) finally resource.close()
val lines: Seq[String] =
using(new BufferedReader(new FileReader("file.txt"))) { reader =>
unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
}

