Use with finally

The following would be nice:

connection.use {
// …
} catch (e: UniquenessConstraintException) {
// fix it
} finally {
// other stuff
}

I know “use” is currently a function and finally and catch are (soft) keywords. Also, I’m aware that I can wrap use in a try block so this is probably not worth the trouble. Just throwing the idea out there to see what you folks think.

Use is meant to omit finally and catch. If you need finally and catch well use it the classic way with a try block instead

1 Like

I just stumble across this as I have a similar need.

Maybe something similar as with runCatching blocks would be hellpful:
having optional .onSuccess and .onFailure blocks.

This would fit into the language syntax and helps in special cases.

Behold, the power of DSLs:

fun <T : Closeable?, R : Any> T.useCatching(
    block: (T) -> R
): UseCatching<T, R> = UseCatching(this, block)

data class UseCatching<T : Closeable?, R : Any>(
    val t: T,
    val block: (T) -> R,
    val catchBlocks: List<CatchBlock<*, T, R>> = emptyList()
) {
    inline infix fun <reified E : Exception> CATCH(noinline catchBlock: T.(E) -> R) =
        UseCatching(t, block, catchBlocks + CatchBlock(E::class, t, catchBlock))

    infix fun FINALLY(finallyBlock: T.() -> Unit): R =
        try {
            t.use(block)
        } catch (e: Exception) {
            catchBlocks.firstNotNullOfOrNull { it.runIfPossible(e) } ?: throw e
        } finally {
            t.finallyBlock()
        }
}

data class CatchBlock<E : Exception, T : Closeable?, R : Any>(
    val exceptionClass: KClass<E>,
    val t: T,
    val block: T.(E) -> R
) {
    fun runIfPossible(e: Exception): R? {
        return if (exceptionClass.isInstance(e)) {
            with(t) {
                @Suppress("UNCHECKED_CAST") block(e as E)
            }
        } else null
    }
}

Usage:

val i = connection.useCatching {
    if (3 == 4) throw ArithmeticException()
    throw NullPointerException()
    23
} CATCH { a: ArithmeticException ->
    println("AE $a"); 42
} CATCH { n: NullPointerException ->
    println("NPE $n"); 0
} FINALLY { println("Finally!") }

Disclaimer: No guarantees, this is just a proof of concept

1 Like

I’m a little wary of this since if one forgets a FINALLY, the entire thing just doesn’t run. It’s also not inline, but I don’t think there’s a way to make it so.

1 Like

I think the question that really needs to be asked is “Why do you need this?” Imo, the point of a finally block is to do any clean up that needs to be done regardless of whether an exception occurs. Usually that’s things like closing connections. Well, with Closeable and AutoCloseable and the use function in Kotlin, all of that stuff is taken care of for us, so what would need to go in the finally block that couldn’t just go after the try / catch? If you have some kind of custom closing logic, perhaps you could create your own custom use function that works on a generic union type or something.

For example:

fun <T, R> T.useDatabase(block: T.() -> R): R where T : DatabaseConnection, DatabaseTransaction = try {
    block()
} finally {
    rollBackTransaction()
    closeConnection()
}
1 Like