Scala for-comprehensions for resource management
Tags: scala
Published: 2022-12-10 18:37:10

---
title: Scala for-comprehensions for resource management
published: true
description:
tags: scala
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-26 17:23 +0000
---
JAVA's ['try-with-resources'](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) statement is its idiomatic answer to the question of safe resource management, as borrow checking is in Rust and the `defer` keyword is in Go. For example:

```java
static String readFirstLineFromFile(String path) throws IOException {
  try (
    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr)
  ) {
      return br.readLine();
  }
}
```

Scala doesn't have language-level support for resource management, it just relies on its standard library, providing a [Using](https://www.scala-lang.org/api/2.13.6/scala/util/Using$.html) utility object. Ported over, the previous example would look like:

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

def readFirstLineFromFile(path: String): Try[String] = Using.Manager { use =>
  val fr = use(new FileReader(path))
  val br = use(new BufferedReader(fr))

  br.readLine()
}
```

Unfortunately, this is pretty clunky. The main problem is that it doesn't provide any type-level support for controlling the resource access. We can easily forget to wrap it in the `use` resource manager and create a leak.

Fortunately, we can do better. We can write a helper that can be used in for-comprehensions that specifically manage `AutoCloseable` resources. Usage looks like:

```scala
def readFirstLineFromFile(path: String): String =
  for
    fr <- AutoClose(new FileReader(path))
    br <- AutoClose(new BufferedReader(fr))
  do
    br.readLine()
```

Now, both the acquired resources are strongly typed and we cannot forget to use the `AutoClose` wrapper. This ensures that they must be closed properly at the end of the scope (in reverse order of opening, just like in Java).

Notice that this looks structurally similar to the Java try-with-resources example. There's a good reason for that–for-comprehensions generalize the idea of binding variables in scope while doing certain things automatically behind the scenes. Which is exactly what 'try-with-resources' is doing but with a special syntax just for this use-case.

Here's the implementation:

```scala
// AutoClose.scala

package util

class AutoClose[R <: AutoCloseable] private (val acquire: () => R) extends AnyVal:
```

The key concept is that the resource being auto-closed must be a subtype of the standard Java `AutoCloseable` interface. This will include everything that will work with Java's 'try-with-resources'.

```scala
  import AutoClose.apply

  def foreach[U](f: R => U): U =
    val resource = acquire()
    try f(resource) finally Option(resource).foreach(_.close())
```

We implement `foreach` in a slightly different way than the normal which is to give it a return type of `Unit`. With a generic return type `U` it becomes possible to use it in a much more general way, e.g. above where we return the first line of the read file, and also to implement the rest of the operations.

Of course, the key here is to use Scala's 'try-finally' to ensure that the resource is safely closed despite any exceptions that may arise. Note the use of `Option(...).foreach(_.close())` to handle the case of the acquired resource being `null`. We sometimes get `null` from Java I/O APIs!

```scala
  def flatMap[R2 <: AutoCloseable](f: R => AutoClose[R2]): AutoClose[R2] =
    foreach(f)

  def map[R2 <: AutoCloseable](f: R => R2): AutoClose[R2] = apply(foreach(f))
```

Together, these operations provide support for using `AutoClose` in for-comprehensions.

```scala
object AutoClose:
  def apply[R <: AutoCloseable](resource: => R): AutoClose[R] =
    new AutoClose(() => resource)
```

And finally, the helper smart constructor in the companion object.

This is a powerful utility to safely handle JVM resources. And the nice thing about it is how easy it is to adopt into the code. Say you're using Apache HttpClient to do web requests:

```scala
val response = httpClient.execute(request)
// do stuff with `response`

// maybe remember to close the `response`
```

We should really ensure there's no resource leak by auto-closing the response after we finish using it:

```scala
import util.AutoClose

for
  response <- AutoClose(httpClient.execute(request))
do
  // stuff with `response`

// after the for-comprehension scope, the `response` is automatically closed
```

On a side note, there are not many tools out there that can statically analyze a Scala codebase and find potential resource leaks. Rust's borrow checking combined with its automatic resource management still has the edge here. In theory there's nothing stopping people from creating resource-safe I/O APIs that enforce the resources are disposed properly. But for now, some manual work will be required.

EDIT: amazingly, I came up with almost the exact same code four years ago: https://www.reddit.com/r/scala/comments/a7047a/a_short_story_about_resource_handling/ebzxsij/