arrow-fx-coroutines / arrow.fx.coroutines / Resource
sealed class Resource<out A>
Resource models resource allocation and releasing. It is especially useful when multiple resources that depend on each other need to be acquired and later released in reverse order.
When a resource is created one can make use of use to run a computation with the resource. The finalizers are then guaranteed to run afterwards in reverse order of acquisition.
Consider the following use case:
import arrow.fx.coroutines.*
object Consumer
object Handle
class Service(val handle: Handle, val consumer: Consumer)
suspend fun createConsumer(): Consumer = Consumer.also { println("Creating consumer") }
suspend fun createDBHandle(): Handle = Handle.also { println("Creating db handle") }
suspend fun createFancyService(consumer: Consumer, handle: Handle): Service =
Service(handle, consumer).also { println("Creating service") }
suspend fun closeConsumer(consumer: Consumer): Unit = println("Closed consumer")
suspend fun closeDBHandle(handle: Handle): Unit = println("Closed db handle")
suspend fun shutDownFancyService(service: Service): Unit = println("Closed service")
//sampleStart
val program = suspend {
val consumer = createConsumer()
val handle = createDBHandle()
val service = createFancyService(consumer, handle)
// use service
// <...>
// we are done, now onto releasing resources
shutDownFancyService(service)
closeDBHandle(handle)
closeConsumer(consumer)
}
//sampleEnd
suspend fun main(): Unit = program.invoke()
Here we are creating and then using a service that has a dependency on two resources: A database handle and a consumer of some sort. All three resources need to be closed in the correct order at the end. However this program is not correct. It does not guarantee release if something failed in between, and keeping track of acquisition order is unnecessary overhead.
That is where Resource
comes in:
import arrow.fx.coroutines.*
object Consumer
object Handle
class Service(val handle: Handle, val consumer: Consumer)
suspend fun createConsumer(): Consumer = Consumer.also { println("Creating consumer") }
suspend fun createDBHandle(): Handle = Handle.also { println("Creating db handle") }
suspend fun createFancyService(consumer: Consumer, handle: Handle): Service =
Service(handle, consumer).also { println("Creating service") }
suspend fun closeConsumer(consumer: Consumer): Unit = println("Closed consumer")
suspend fun closeDBHandle(handle: Handle): Unit = println("Closed db handle")
suspend fun shutDownFancyService(service: Service): Unit = println("Closed service")
//sampleStart
val resourceProgram = suspend {
Resource(::createConsumer, ::closeConsumer)
.zip(Resource(::createDBHandle, ::closeDBHandle))
.flatMap { (consumer, handle) ->
Resource({ createFancyService(consumer, handle) }, { service -> shutDownFancyService(service) })
}.use { service ->
// use service
// <...>
Unit
}
}
//sampleEnd
suspend fun main(): Unit = resourceProgram.invoke()
Allocate | class Allocate<A> : Resource <A> |
Bind | class Bind<A, B> : Resource <B> |
Defer | class Defer<A> : Resource <A> |
ap | fun <B> ap(ff: Resource <(A) -> B>): Resource <B> |
flatMap | Create a new resource B from a resource A by mapping f.fun <B> flatMap(f: (A) -> Resource <B>): Resource <B> |
map | fun <B> map(f: (A) -> B): Resource <B> |
map2 | fun <B, C> ~~map2~~(other: Resource <B>, combine: (A, B) -> C): Resource <C> |
use | Use the created resource When done will run all finalizerssuspend infix fun <B> use(f: suspend (A) -> B): B |
zip | fun <B, C> zip(other: Resource <B>, combine: (A, B) -> C): Resource <C> fun <B> zip(other: Resource <B>): Resource < Pair <A, B>> fun <B, C, D> zip(b: Resource <B>, c: Resource <C>, map: (A, B, C) -> D): Resource <D> fun <B, C, D, E> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, map: (A, B, C, D) -> E): Resource <E> fun <B, C, D, E, F, G> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, map: (A, B, C, D, E) -> G): Resource <G> fun <B, C, D, E, F, G, H> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, f: Resource <F>, map: (A, B, C, D, E, F) -> G): Resource <G> fun <B, C, D, E, F, G, H> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, f: Resource <F>, g: Resource <G>, map: (A, B, C, D, E, F, G) -> H): Resource <H> fun <B, C, D, E, F, G, H, I> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, f: Resource <F>, g: Resource <G>, h: Resource <H>, map: (A, B, C, D, E, F, G, H) -> I): Resource <I> fun <B, C, D, E, F, G, H, I, J> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, f: Resource <F>, g: Resource <G>, h: Resource <H>, i: Resource <I>, map: (A, B, C, D, E, F, G, H, I) -> J): Resource <J> fun <B, C, D, E, F, G, H, I, J, K> zip(b: Resource <B>, c: Resource <C>, d: Resource <D>, e: Resource <E>, f: Resource <F>, g: Resource <G>, h: Resource <H>, i: Resource <I>, j: Resource <J>, map: (A, B, C, D, E, F, G, H, I, J) -> K): Resource <K> |
defer | fun <A> defer(f: suspend () -> Resource <A>): Resource <A> |
fromClosable | Creates a Resource from an Closeable, which uses Closeable.close for releasing.fun <A : Closeable > fromClosable(f: suspend () -> A): Resource <A> |
fromExecutor | Creates a single threaded CoroutineContext as a Resource. Upon release an orderly shutdown of the ExecutorService takes place in which previously submitted tasks are executed, but no new tasks will be accepted.fun fromExecutor(f: suspend () -> ExecutorService ): Resource < CoroutineContext > |
invoke | Construct a Resource from a allocating function acquire and a release function release.operator fun <A> invoke(acquire: suspend () -> A, release: suspend (A, ExitCase ) -> Unit ): Resource <A> operator fun <A> invoke(acquire: suspend () -> A, release: suspend (A) -> Unit ): Resource <A> |
just | Create a Resource from a pure value A.fun <A> just(r: A): Resource <A> |
singleThreadContext | Creates a single threaded CoroutineContext as a Resource. Upon release an orderly shutdown of the ExecutorService takes place in which previously submitted tasks are executed, but no new tasks will be accepted.fun singleThreadContext(name: String ): Resource < CoroutineContext > |
tailRecM | fun <A, B> tailRecM(a: A, f: (A) -> Resource <Either<A, B>>): Resource <B> |
Do you like Arrow?
✖