Try-with-resources: the Kotlin way?


#1

I must admit I quite like try-with-resources which was introduced in Java 7. I tried to find what the Kotlin way is, but with just little success, nevertheless I found an article on this topic: http://blog.jonnyzzz.name/2013/12/try-with-resource-in-java.html

The article is quite interesting. The points about the solution that Java chose are notable, unfortunately the solution for Kotlin is not satisfying at all, I would say, because it can’t handle even the use cases where the Java solution works very well (i.e., safe resource allocation and disposal, especially with multiple resources). Maybe I just don’t see something, being a beginner, but I guess that there must be some support from the compiler to achieve at least the same level of usability and consistency. The main pain point of the solution from the article is that it needs an initialized variable before the using function and it can’t cope safely with multiple resources at once, which is IMO one of the main selling points of the Java solution. Multiple resources are difficult to handle with just simple try-finally.

I’d be happy if Kotlin had a construct for safe resource acquisition and release that would be at least as good as what Java provides.  If I may, I’d like to share an idea/proposal of such a construct, using some examples:

Firstly, the basic case, similar to Java solution: opens two streams, assigns the objects into local variables (I guess that val can be omitted, so just the name of the variable is good enough):
using (is = newInputStream(“file.txt”), os = newOutputStream(“copy.txt”)) { // If os fails, is would be closed anyway

  // copy the file content
} // Closes all acquired resources

Here I assume that the resource has a method of a well-known name, e.g. close to reuse AutoCloseable directly (it is similar to the solution used for tuples with the componentN methods). But sometimes the method might be missing or some different handling is needed. Therefore the more general form might be available:
using (original = setThreadContextClassLoader(desired) with { setThreadContextClassLoader(original) }) {

  // Some code
}

The with keyword (or any other suitable, for instance recycling lengthy finally) allows specifying custom cleanup. In this simple case with just one resource, it does not bring much more than simple try-finally, besides the local variable declaration. Still, there are things to improve. One of the deficiencies of the Java way, according to the article, is when optional catch and finally blocks are executed (after the clean up code). What if the using clause could be combined with catch and finally, too?
using (context = makeContext()) try {
  // Some code
} catch (e: IllegalStateException) {
  // Resolved before closing context, so we can use it here
} finally {
  // Optional, like the catch, and again before closing the context
}

In the case that the catch/finally should apply to exceptions thrown by the resource acquisition or release, just change the order:
try using (context = makeContext()) {
  // Some code
} catch (e: IllegalStateException) {
  // Resolved after closing context, so we can’t use it here
} finally {
  // Optional, like the catch, and again after closing the context
}

Well, this latter case is just sugar and the same thing could be achieved with extra parenthesis around the using part. But this syntax saves one nesting level and makes the nesting consistent with the former case.

Another point is that the name of the variable might be omitted if the default handling (using close method) is good enough and the variable is not used otherwise, because just the allocation and release side effects are important:
using (logScope(“Operation A”)) {
  // Some code executes

} // Here the object created by logScope function is released

A note for the end: some of the cases look simple at the first glance. But try to think over following: what happens if the allocation throws and exception? What if there are more resources and any of them may thrown an exception? What happens when a release of any resource throws an exception? Or when a resource allocation returns null? And we always want that all allocated resources will be released, no matter what happened. And of course, it would be nice if no exception is lost – using suppressed exceptions from Java 7 is definitely worth that. Taking all this into account, I believe that it is the job for compiler to generate the proper handling.

What are your opinions?


#2

Petr,

Thanks for such a detailed post! We are definitely looking into try-with-resource pattern but we don’t want to add special constructs to the language until absolutely necessary. So, we are experimenting with constructs like this:

val text = using {
  val is = InputStream(…).autoClose()
  val os = OutputStream(…).autoClose()
  “text”

}

using introduces “ResourceContext” and autoClose adds items into it. In finally block inside “using” it closes them in reverse order. So, if OutputStream throws, InputStream will still be destroyed.
there could be variants like this:

using(resource1, resource2) {} // when you already have resources

InputStream(…).using { stream -> … } // for a shorter syntax with one resource

This is being developed right now with the rest of IO part of stdlib, stay tuned!


#3

Great :) I'm looking forward to have the possibility to manage resources safely. Safety first ;)

I’m curious how the resource context would work. Obviously, I don’t know Kotlin well enough to see the way how to provide an instance of the resource context (assuming it is just a library class with some hooks called by the autoClose and using functions) to invocations of autoClose within the appropriate scope. Knowing this technique would be… hm, inspiring, I would say. For example, the same technique could, for instance, support explicit library locks (safe lock-unlock) in more complex deadlock-preventing locking scenarios.

I assume that the facility can cope with exceptions thrown by the resource release function or the using block can be nested: for instance I want split a file into several other files sequentially, so I have a using block for the source source, a loop in the block and in the loop again a nested using block for the target file.

Perhaps yet one more question, remotely related: Java 7 introduced suppressed exceptions, which works exceptionally well with try-with-resources. Will that be supported in Kotlin as well?

I will definitely stay tuned!


#4

Basically the technique is like this (writing code right in the post, so forgive possible typos)

class ResourceContext : Closeable {   val closeables = arrayListOf<Closeable>()   fun <T:Closeable> T.autoClose() : T { // yes, instance function with receiver   closeables.add(this) // "this" is receiver, not class. "this@ResourceContext" would be class instance.   return this   }   fun close() {   closeables.reverse().forEach { it.close() }   } } inline fun using(body: ResourceContext.()->Unit) {   val context = ResouceContext()   try {   context.body() // body is function with receiver, so we call it on ResouceContet instance   finally {   context.close()   } } fun fn() {   using {  // this block is lambda passed into body parameter.   // "this" inside this lambda is "ResouceContext", because it is lambda with receiver.   val closeable = InputStream(...).autoClose() // calls autoClose() on lambda's this, passes InputStream as receiver.   } }


#5

We are working on Java 7 and Java 8 support, it needs "multi-targeting" feature which is quite complex. Depending on the target platform, standard library should behave differently in several places, so we are designing how should we approach differences in platforms. Also, JavaScript.


#6

I see :O The instance function with the receiver is the trick I didn't know. Hm, that's smart. I can now imagine how it can be integrated with decent exception handling or how to use it for other cases, like the locks. And it seems that it there is no problem with nesting either.

Thank you for the explanation :slight_smile: