scala “使用”功能

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2395984/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 01:59:32  来源:igfitidea点击:

"using" function

scalausingtuples

提问by Albert Cenkier

I've defined 'using' function as following:

我已经定义了“使用”功能如下:

def using[A, B <: {def close(): Unit}] (closeable: B) (f: B => A): A =
  try { f(closeable) } finally { closeable.close() }

I can use it like that:

我可以这样使用它:

using(new PrintWriter("sample.txt")){ out =>
  out.println("hellow world!")
}

now I'm curious how to define 'using' function to take any number of parameters, and be able to access them separately:

现在我很好奇如何定义“使用”函数来接受任意数量的参数,并能够分别访问它们:

using(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt")){ (in, out) =>
  out.println(in.readLIne)
}

回答by Lambda Fairy

Someone has already done this—it's called Scala ARM.

有人已经这样做了——它被称为Scala ARM

From the readme:

从自述文件:

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

回答by Xavier Guihot

Starting Scala 2.13, the standard library provides a dedicated resource management utility: Using.

开始Scala 2.13,标准库提供了一个专用的资源管理实用程序:Using.

More specifically, the Using#Managercan be used when dealing with several resources.

更具体地说,Using#Manager可以在处理多个资源时使用。

In our case, we can manage different resources such as your PrintWriteror BufferedReaderas they both implement AutoCloseable, in order to read and write from a file to another and, no matter what, close both the input and the output resource afterwards:

在我们的例子中,我们可以管理不同的资源,例如你的PrintWriterBufferedReader它们都实现的AutoCloseable,以便从一个文件读取和写入另一个文件,无论如何,之后关闭输入和输出资源:

import scala.util.Using
import java.io.{PrintWriter, BufferedReader, FileReader}

Using.Manager { use =>

  val in  = use(new BufferedReader(new FileReader("input.txt")))
  val out = use(new PrintWriter("output.txt"))

  out.println(in.readLine)
}
// scala.util.Try[Unit] = Success(())

回答by huynhjl

I've been thinking about this and I thought may be there was an other way to address this. Here is my take on supporting "any number" of parameters (limited by what tuples provide):

我一直在考虑这个问题,我想可能有其他方法可以解决这个问题。这是我对支持“任意数量”参数的看法(受元组提供的限制):

object UsingTest {

  type Closeable = {def close():Unit }

  final class CloseAfter[A<:Product](val x: A) {
    def closeAfter[B](block: A=>B): B = {
      try {
        block(x);
      } finally {
        for (i <- 0 until x.productArity) {
          x.productElement(i) match { 
            case c:Closeable => println("closing " + c); c.close()
            case _ => 
          }
        }
      }
    }
  }

  implicit def any2CloseAfter[A<:Product](x: A): CloseAfter[A] = 
    new CloseAfter(x)

  def main(args:Array[String]): Unit = {
    import java.io._

    (new BufferedReader(new FileReader("in.txt")), 
     new PrintWriter("out.txt"),
     new PrintWriter("sample.txt")) closeAfter {case (in, out, other) => 
      out.println(in.readLine) 
      other.println("hello world!")
    }
  }
}

I think I'm reusing the fact that 22 tuple/product classes have been written in the library... I don't think this syntax is clearer than usingnested using(no pun intended), but it was an interesting puzzle.

我想我正在重用库中已经编写了 22 个元组/产品类的事实......我认为这种语法并不比使用嵌套using(没有双关语)更清晰,但这是一个有趣的难题。

edit: replaced the val assignment with case (in, out, other)as suggested by retronym.

编辑:case (in, out, other)按照retronym 的建议替换val 分配。

回答by Peter Ertl

using structural typing seems like a little overkill since java.lang.AutoCloseableis predestined for usage:

使用结构类型似乎有点矫枉过正,因为java.lang.AutoCloseable注定要使用:

  def using[A <: AutoCloseable, B](resource: A)(block: A => B): B =
    try block(resource) finally resource.close()

or, if you prefer extension methods:

或者,如果您更喜欢扩展方法:

  implicit class UsingExtension[A <: AutoCloseable](val resource: A) extends AnyVal {
    def using[B](block: A => B): B = try block(resource) finally resource.close()
  }

using2 is possible:

using2 是可能的:

  def using2[R1 <: AutoCloseable, R2 <: AutoCloseable, B](resource1: R1, resource2: R2)(block: (R1, R2) => B): B =
    using(resource1) { _ =>
      using(resource2) { _ =>
        block(resource1, resource2)
      }
    }

but imho quite ugly - I would prefer to simply nest these using statements in the client code.

但恕我直言很丑陋 - 我宁愿简单地将这些 using 语句嵌套在客户端代码中。

回答by Dan Shryock

Here is an example that allows you to use the scala for comprehension as an automatic resource management block for any item that is a java.io.Closeable, but it could easily be expanded to work for any object with a close method.

下面是一个示例,它允许您使用 Scala 进行理解作为任何 java.io.Closeable 项的自动资源管理块,但它可以很容易地扩展为使用 close 方法的任何对象。

This usage seems pretty close to the using statement and allows you to easily have as many resources defined in one block as you want.

这种用法似乎非常接近 using 语句,并允许您轻松地在一个块中定义任意数量的资源。

object ResourceTest{
  import CloseableResource._
  import java.io._

  def test(){
    for( input <- new BufferedReader(new FileReader("/tmp/input.txt")); output <- new FileWriter("/tmp/output.txt") ){
      output.write(input.readLine)
    }
  }
}

class CloseableResource[T](resource: =>T,onClose: T=>Unit){
  def foreach(f: T=>Unit){
    val r = resource
    try{
      f(r)
    }
    finally{
      try{
        onClose(r)
      }
      catch{
        case e =>
          println("error closing resource")
          e.printStackTrace
      }
    }
  }
}

object CloseableResource{
  implicit def javaCloseableToCloseableResource[T <: java.io.Closeable](resource:T):CloseableResource[T] = new CloseableResource[T](resource,{_.close})
}

回答by Rex Kerr

Unfortunately, there isn't support for arbitrary-length parameter lists with arbitrary types in standard Scala.

不幸的是,标准 Scala 不支持具有任意类型的任意长度参数列表。

You might be able to do something like this with a couple of language changes (to allow variable parameter lists to be passed as HLists; see herefor about 1/3 of what's needed).

您可以通过一些语言更改来执行类似的操作(以允许将可变参数列表作为 HList 传递;请参阅此处了解大约 1/3 所需的内容)。

Right now, the best thing to do is just do what Tuple and Function do: implement usingN for as many N as you need.

现在,最好的办法就是做 Tuple 和 Function 所做的事情:根据需要为尽可能多的 N 实现 usingN。

Two is easy enough, of course:

两个很简单,当然:

def using2[A, B <: {def close(): Unit}, C <: { def close(): Unit}](closeB: B, closeC: C)(f: (B,C) => A): A = {
  try { f(closeB,closeC) } finally { closeB.close(); closeC.close() }
}

If you need more, it's probably worth writing something that'll generate the source code.

如果您需要更多,可能值得编写一些可以生成源代码的东西。

回答by Espen Brekke

It is a good idea to detatch the cleanup algorithm from the program path.

从程序路径中分离清除算法是个好主意。

This solution lets you accumulate closeables in a scope.
The scope cleanup will happen on after the block is executed, or the scope can be detached. The cleaning of the scope can then be done later.

此解决方案可让您在范围内累积可关闭项。
作用域清理将在块执行后发生,或者作用域可以被分离。然后可以稍后进行内窥镜的清洁。

This way we get the same convenience whitout being limited to single thread programming.

这样我们就可以获得同样的便利,而不受限于单线程编程。

The utility class:

实用程序类:

import java.io.Closeable

object ManagedScope {
  val scope=new ThreadLocal[Scope]();
  def managedScope[T](inner: =>T):T={
    val previous=scope.get();
    val thisScope=new Scope();
    scope.set(thisScope);
    try{
      inner
    } finally {
      scope.set(previous);
      if(!thisScope.detatched) thisScope.close();
    }
  }

  def closeLater[T <: Closeable](what:T): T = {
    val theScope=scope.get();
    if(!(theScope eq null)){
      theScope.closeables=theScope.closeables.:+(what);
    }
    what;
  }

  def detatchScope(): Scope={
    val theScope=scope.get();
    if(theScope eq null) null;
    else {
      theScope.detatched=true;
      theScope;
    }
  }
}

class Scope{
  var detatched=false;
  var closeables:List[Closeable]=List();

  def close():Unit={
    for(c<-closeables){
      try{
        if(!(c eq null))c.close();
      } catch{
        case e:Throwable=>{};
      }
    }
  }
}

The usage:

用法:

  def checkSocketConnect(host:String, portNumber:Int):Unit = managedScope {
    // The close later function tags the closeable to be closed later
    val socket = closeLater( new Socket(host, portNumber) );
    doWork(socket);
  }

  def checkFutureConnect(host:String, portNumber:Int):Unit = managedScope {
    // The close later function tags the closeable to be closed later
    val socket = closeLater( new Socket(host, portNumber) );
    val future:Future[Boolean]=doAsyncWork(socket);

    // Detatch the scope and use it in the future.
    val scope=detatchScope();
    future.onComplete(v=>scope.close());
  }

回答by linehrr

here is my solution to the resource management in Scala:

这是我在 Scala 中的资源管理解决方案:

  def withResources[T <: AutoCloseable, V](r: => T)(f: T => V): V = {
    val resource: T = r
    require(resource != null, "resource is null")
    var exception: Throwable = null
    try {
      f(resource)
    } catch {
      case NonFatal(e) =>
        exception = e
        throw e
    } finally {
      closeAndAddSuppressed(exception, resource)
    }
  }

  private def closeAndAddSuppressed(e: Throwable,
                                    resource: AutoCloseable): Unit = {
    if (e != null) {
      try {
        resource.close()
      } catch {
        case NonFatal(suppressed) =>
          e.addSuppressed(suppressed)
      }
    } else {
      resource.close()
    }
  }

I used this in multiple Scala apps including managing resources in Spark executors. and one should be aware that we are other even better ways to manage resource like in CatsIO: https://typelevel.org/cats-effect/datatypes/resource.html. if you are ok with pure FP in Scala.

我在多个 Scala 应用程序中使用了它,包括在 Spark 执行器中管理资源。并且应该知道,我们还有其他更好的方法来管理资源,例如 CatsIO:https://typelevel.org/cats-effect/datatypes/resource.html 。如果您对 Scala 中的纯 FP 没问题。

to answer your last question, you can definitely nest the resource like this:

要回答您的最后一个问题,您绝对可以像这样嵌套资源:

withResource(r: File)(
  r => {
    withResource(a: File)(
      anotherR => {
        withResource(...)(...)
      }
    )
  }
)

this way, not just that those resources are protected from leaking, they will also be released in the correct order(like stack). same behaviour like the Resource Monad from CatsIO.

这样,不仅可以保护这些资源免于泄漏,还可以按照正确的顺序(如堆栈)释放它们。与 CatsIO 的 Resource Monad 相同的行为。

回答by samthebest

This solution doesn't quitehave the syntax you desire, but I think it's close enough :)

这种解决方案并不十分有你想要的语法,但我认为这是足够接近:)

def using[A <: {def close(): Unit}, B](resources: List[A])(f: List[A] => B): B =
    try f(resources) finally resources.foreach(_.close())

using(List(new BufferedReader(new FileReader("in.txt")), new PrintWriter("out.txt"))) {
  case List(in: BufferedReader, out: PrintWriter) => out.println(in.readLine())
}

Of course the down side is you have to type out the types BufferedReaderand PrintWrterin the using block. You might be able to add some magic so that you just need List(in, out)by using multiple ORed type boundsfor type A in using.

当然,不利的一面是您必须在 using 块中输入类型BufferedReaderPrintWrter。您也许可以添加一些魔法,以便您只需要List(in, out)在 using 中为类型 A使用 多个 ORed 类型边界

By defining some pretty hacky and dangerous implicit conversions you can get around having to type List(and another way to get around specifying types for the resources), but I haven't documented the detail as it's too dangerous IMO.

通过定义一些非常棘手和危险的隐式转换,您可以避免必须键入List(以及另一种避免为资源指定类型的方法),但我没有记录详细信息,因为它在 IMO 中太危险了。