-
Notifications
You must be signed in to change notification settings - Fork 50
Basic Usage
The Scala ARM library provides three “modes” of operations:
- Imperative style resource management functions.
- A monadic style resource management class.
- A delimited continuation style API.
The Scala ARM library allows users to ensure opening closing of resources within blocks of code using the managed method. This is easiest to accomplish with a for expression and the managed method defined on scala.resource:
import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}
The managed method essentially takes an argument of “anything that has a close or dispose method” and constructs a new ManagedResource object. This object has a foreach method which can be used inside of the for expression. The scala-arm library provides a very flexible mechanism for customising the treatment of resource types, using a type class trait. Please read the section on the Resource type class trait for more information.
This style of usage will ensure that the file input stream is closed at the end of the for expression. In the event of an exception, the originating exception (from inside the for block) will be thrown and any exceptions thrown while closing the resource will be suppressed. The benefits of using for expressions is that multiple resources can be managed together. For example, one can do the following:
import resource._
// Copy input into output.
for(input <- managed(new java.io.FileInputStream("test.txt");
output <- managed(new java.io.FileOutputStream("test2.txt")) {
val buffer = new Array[Byte](512)
while(input.read(buffer) != -1) {
output.write(buffer);
}
}
There is a convenience notation for those who don’t like using for comprehensions:
import resource._
managed(DriverManager.getConnection(url, username, password)) acquireAndGet {
connection =>
// Something that uses connection
}
The scala-arm library defined a monadic like container ManagedResource. This container defines map and flatMap interfaces. It can be constructed using the managed method defined on scala.resource. The map and flatMap methods are defined specially, but in generally they will do what you expect them to.
The map method will take a transformation of the raw resource type and return a new managed resource object of the transformed type. Let’s see an example:
import resource._
val first_ten_bytes = managed(new FileInputStream("test.txt")) map {
input =>
val buffer = new Array[Byte](10)
input.read(buffer)
buffer
}
The ManagedResource class also defines mechanisms for extracting data outside of the monadic container after the container has been mapped or flatMapped. This is done through the opt or either methods. Both methods attempt to acquire the resource and run all transformations on the resource. They then close the resource and return a result. In the case of an error, the opt method will return an empty option. The either method will return an Either where the right side is defined and contains the exceptions seen during the execution of the transformations or closing the resource.
scala> first_ten_bytes.opt.get
res1: Array[Byte] = Array(72, 65, 73, 32, 10, 85, 10, 87, 85, 82)
scala> first_ten_bytes.either.right.get
res2: Array[Byte] = Array(72, 65, 73, 32, 10, 85, 10, 87, 85, 82)
The flatMap method can be used to ensure that applying a transformation of an embedded resource to another ManagedResource will create a ManagedResource] instead of a ManagedResource[ManagedResource[].