What Automatic Resource Management alternatives exist for Scala?

后端 未结 9 1325
半阙折子戏
半阙折子戏 2020-12-02 03:57

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 a

相关标签:
9条回答
  • 2020-12-02 04:36

    Here's James Iry solution using continuations:

    // 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:

    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
    }
    
    0 讨论(0)
  • 2020-12-02 04:41

    For now Scala 2.13 has finally supported: try with resources by using Using :), Example:

    val lines: Try[Seq[String]] =
      Using(new BufferedReader(new FileReader("file.txt"))) { reader =>
        Iterator.unfold(())(_ => Option(reader.readLine()).map(_ -> ())).toList
      }
    

    or using Using.resource avoid 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 Using doc.

    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.

    0 讨论(0)
  • 2020-12-02 04:41

    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

    This library supports three styles of usage (currently):

    1) Imperative/for-expression:

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

    2) Monadic-style

    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

    Here's an "echo" tcp server:

    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.

    0 讨论(0)
提交回复
热议问题