Coding demands a lot of attention to detail even if it is not the most complex of operations. Every resource and stream needs to be closed so Senior Software Engineer Michał Matłoka looks at how exactly you can design code in different programming languages to ensure it is always properly closed.
'Reading a file, handling a stream of data, theoretically those are not ultimately complex operations, but they require some special attention. Every stream, every resource needs to be closed. Closed to free descriptors, memory, or maybe just a TCP port. How to design the code, so that a resource will always be properly closed, even when the programmer forgets?
Going back in time
For start, let’s remind ourselves how resource handling looked like in the C-times. First, you start with opening the file:
#include <stdio.h>
// ...
FILE * file = fopen("test.txt", "r");
Returned value, 'file', allows to control the stream of data. Usually you would perform some 'fread' ‘s or 'getc' to retrieve the file content, and end the whole operation with:
fclose(file);
Perfect solution? Not really, you have to remember to close the file manually, even when some intermediate operations will return errors.
Java Virtual Machine?
Does it get better in Java? Well, depends on a version. In older ones you still have to manually close resources.
BufferedReader br = new BufferedReader(new FileReader("test.txt"));
try {
String line = br.readLine();
// ...
} finally {
br.close();
}
However, you get the 'try-catch-finally' statements guaranteeing that 'finally' block will be automatically executed even when exception is thrown. But, from Java 7 you can leverage the “try-with-resources” construction:
try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line = br.readLine();
// ...
}
Better? Definitely! Java 7 introduced an 'AutoCloseable' interface. Every class implementing it can be used with “try-with-resources”. You don’t need to manually close your file, it will get auto closed after the processing block ends.
Side note: From Java 7 you can read files easier e.g. just by using 'Files.readAllLines(...)'
Scala
How does it look in Scala, well…
val source = scala.io.Source.fromFile("test.txt")
val lines = try source.mkString finally source.close()
You can use other classes instead of the 'BufferedReader', but how about the “try-with-resources”? Unfortunately there is no built-in, ready to use equivalent. You can implement it on your own, however additional project was created to solve this issue, the 'scala-arm' (Scala Automatic Resource Management).
import resource._
val mr = managed(new java.io.FileInputStream("test.txt")
for(r <- mr ) {
// do some reading...
}
'scala-arm' APIs produce the 'ManagedResource' objects. After the 'for' block ends, the resource will get closed.
Advantages? Resource gets auto closed and what is more, your API can return object, which after processing ('foreach' or 'map') will close automatically. API user does not have to worry about that!
FP world
Functional Programming world created the Bracket pattern already many years ago. It’s intended to use with resources which require acquiring and releasing actions. Let’s take a look how it looks in Cats-effect:
IO(new BufferedReader(new FileReader(file))).bracket {
in =>
IO(in.readLine())
} { in =>
IO(in.close())
}
The first bracket block is responsible for resource usage, and the second one for closing. Advantages? It’s pure of course ;) It works with cats 'IO' Monad (in short: type designed to handle operations which may include unpure side effects) and the release function is guaranteed to run even when errors occur. Cats library offers however, one more class for resource handling, named… 'Resource'.
import cats.effect._
val acquire = IO { // it touches file system so let's wrap it in IO
scala.io.Source.fromString("Hello world")
}
Resource.fromAutoCloseable(acquire)
.use(source => IO(println(source.mkString))) //use ~ map on resource
.unsafeRunSync() // it is lazily evaluated, you need to run
'Resource' supports the java 'AutoCloseable' which is quite handy. You don’t need to define the 'release'/'close' methods, only the acquirement and the actual usage. After processing is finished, the underlying resource will get closed.
Summary
Resource handling evolved differently in various languages. For sure the 'AutoCloseable' interface added in Java 7 is a worthy addition. However due to the backward compatibility with Java 6 some libraries are still not using it. On the other hand it’s also good to implement approach, in which developer leveraging your API does not have to worry about resource handling — the approaches like the 'Bracket' pattern looks quite handy. It’s worth to notice that 'Bracket' is not cats-only, and does not originate from cats— you can find it in scalaz, FS2 and of course Haskell. There is no single answer how you should treat your resources, as usually - it depends. But remember about users of your APIs.'
This article was written by Michał Matłoka and posted originally on SoftwareMill Blog.