0

I currently face the problem of correctly closing resources that never leave their containing Either.

The relevant code looks something like this:

object SomeError
class MyRes : AutoCloseable { [...] }

fun createRes(): Either<SomeError, MyRes> { [...] }

fun extractData(res: MyRes): String { [...] }

fun theProblem(): Either<SomeError, String> {
    return createRes()
        .map { extractData(it) }
}

What is the most idiomatic way of closing the created MyRes? Closing it before that map prevents extractData from accessing it, and after the map I can't access it anymore via Either's operations. Closing it in extractData severely limits composability.

Currently I have an external List<AutoCloseable> that I iterate over after all the computations, but that can't be the intended way.

I am open to using Arrow Fx (e.g. Resource) if that helps, but I haven't found anything on how to combine Either and Resource in an elegant way.

Kolja
  • 1,197
  • 9
  • 17

2 Answers2

2

It's possible to combine the either and Resource safely.

object SomeError
class MyRes : AutoCloseable { [...] }

fun createRes(): Resource<Either<SomeError, MyRes>> { [...] }
fun extractData(res: MyRes): String { [...] }

suspend fun solution(): Either<SomeError, String> = either {
  createRes().use { either: Either<SomeError, MyRes> ->
    val res = either.bind()
    val string = extractData(res)
    // call other Either code + `bind()` safely here
    [...]

  } // <-- MyRes will automatically close here
}

If in this code you encounter Either.Left and you call bind() on it the Resource will first close, because we jump outside of use, and then either will return the encountered Either.Left.

nomisRev
  • 1,881
  • 8
  • 15
  • Does `createRes` have to provide the release action (= the call to `close()`) when creating the resource, or are there special implementation that handle that automatically? Also, this does not completely cover my problem, since this still moves the resource handling _outside_ the either chain. – Kolja Apr 29 '22 at 05:15
  • 1
    You can use `Resource.fromAutoCloseable { MyRes() }` and that can automatically close resource for any type `A : AutoCloseable`. A similar constructor exists for `Closeable` and otherwise, you can manually specify the `close`/`shutdown` through the regular constructor. – nomisRev Apr 29 '22 at 12:31
  • Ah, that makes more sense than my intuition of a specialization for `Either`. I've tried your suggestion and it works and is fine for my current use case. I was hoping (my question is not worded very well) to find a solution for more complex cases (think `String->HTTPRequest->String`, where the `AutoCloseable` never exists outside the `Either`-`map`-chain), but I guess I'll have to break those up into smaller pieces. – Kolja May 02 '22 at 13:59
0

One possible solution I found was wrapping the block passed to map:

fun <B : AutoCloseable, C> andClose(f: (B) -> C): (B) -> C =
    { b: B -> b.use { f(b) } }


fun theProblemSlightlySolved(): Either<SomeError, String> {
    return createRes()
        .map(andClose { extractData(it) })
}
Kolja
  • 1,197
  • 9
  • 17