Concise way to manage resource with exception in kotlin


#1

Hi, new to kotlin here. I just stumble with managing resource with exception.

When doing IO operation, IOException is just normal and expected (internet connection closed suddenly, disk full, etc), so it must caught and properly handled.

In Java we can use try with resource to make a method never throw IOException and resource will properly closed when the method return.

Simplified example in Java

public ParseResult parse(File file) {
    try (InputStream stream = new FileInputStream(file)) {
        // do something with stream, might throw IOExcpetion here
        return ParseResult.success(...);
    } catch(IOException e) {
        return ParseResult.failed(e);
    }
}

Same functionality in kotlin that I can come up looks more verbose, and somehow uglier because deeper level of {}

fun parse(file: File): ParseResult {
    return try {
        file.inputStream().use {
            // do something with stream, might throw IOExcpetion here
            ParseResult.success(...)
        }
    } catch(e: IOException) {
        ParseResult.failed(e)
    }
}

Are there more concise and idiomatic way to manage resource that might throw exception in kotlin? Am I missing something?


#2

On the JVM there is use.


#3

On the JVM there is use

Yes, I already know use (right in the example posted above). But nesting use inside try - catch looks more verbose than Java.


#4

Doh, sorry. I just glanced over the examples.

But in that case, there is no more concise syntax. That should not be a problem though, because it is already very concise (the only things that are “redundant” are the 2 keywords and some braces), and it is usually not used in a lot of places.

If you do use it a lot, you can write an extension function that contains the use invocation and try-catch statement.


#5

Doh, sorry. I just glanced over the examples.

It is okay :wink:

If you do use it a lot, you can write an extension function that contains the use invocation and try-catch statement.

Hmm… thanks for your suggestion. I will consider that option.

I thought something similar to try-with-resource already exists in the standard library.

edit

I have tried using extension function and ended up creating multiple extension function for File, URL, etc, which is IMHO not flexible enough (or maybe I just don’t know proper way to use it)


#6

Succes/Fail is a functional Try

You can optionally define:

fun Closeable.tryUse(block: () -> R) = Try{ use{ block() } }

Try here: https://github.com/MarioAriasC/funKTionale/blob/master/funktionale-try/src/main/kotlin/org/funktionale/tries/Try.kt


#7

Thanks for pointing out functional Try.

Functional try don’t have functionality to automatically release / close() the resource, and must be handled manually.

val Closeable.tryUse(block: () -> R) = Try{ use{ block() } }

The problem is, acquiring Closeable itself might throw IOException.


#8

I am experimenting with functional try idea above, end up with idea of LazyTry

class LazyTry<C: Closeable>(private val create: () -> C) {

    fun <T> use(transform: (C) -> T): Result<T> {
        return try {
            create().use { transform(it) }.let { Result.success(it) }
        } catch (e: IOException) {
            Result.failed(e)
        }
    }
}

It will lazily open the resource, at the time LazyTry::use() method invoked.

And using it will look something like this

LazyTry { file.inputStream() }.use {
    it.reader().readText()
}.onSuccess {
    // consume the result when success
    println(it)
}.onFailed {
    // handle the exception when failed
    logger.info("failed to read file", it)
}

Resource will be closed automatically and exception will be caught, so it mimic Java try with resource.

What do you think? Is it acceptable Kotlin style?


#9

I don’t like it, because you seem to be unable to write something like

val foo = LazyTry{…}

Also it would be better if this was a function call rather than a constructor call (lower case) because you don’t really want to be aware of the internal creation of the Result object.

Also needs a finally block.

I feel this really needs a language feature and all the attempts here are crude workarounds.


#10

Thanks for your response

… you seem to be unable to write something like

val foo = LazyTry{…}

Sorry, I am not quite sure I know what you mean

val openLater = LazyTry { file.inputStream() }

Will create an object openLater without actually opening the file, and can be use()-ed multiple time.

openLater.use { read(it) }
openLater.use { doSomethingWith(it) }
openLater.use { doOtherThingWith(it) }

Each time use invoked, the InputStream will be created and closed when the use block end

Also it would be better if this was a function call rather than a constructor call (lower case)

Yes, good point. Maybe top level function like this will do

fun <C: Closeable> tryWith(create: () -> C) = LazyTry(create)

Then, my original question can be rewritten in kotlin like this

fun parse(file: File) : Result {
    return tryWith {
        file.inputStream()
    }.use {
        doSomethingWith(it)
    }
}

Also needs a finally block.

As far as I understand, the purpose of finally is to clean up resources, which is already handled by Closeable.use extension method

I feel this really needs a language feature and all the attempts here are crude workarounds.

Totally agree with you!
Kotlin is supposed to be “Better Java” (or so I heard). This problem already solved since Java 7!. Too bad it is not implemented yet.


#11

I would like to do something like

readLock.lock()
val jsonObject = try(val inputStream = InputStream(...) ) {
 Json.read(inputStream) 
} 
catch(e: IOException) {
 Logger.log(e)
 throw e
} 
finally {
 readLock.unlock()
} 

As for the finally block I often found that you need to do something in addition to closing the Closable object, like release a ReadWriteLock as in the example or log a message or whatever. Don’t have a specific example right now but I found it very handy to be able to use try-with-resources with finally in Java.


#12

Use withLock

http://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/java.util.concurrent.locks.-lock/with-lock.html