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. Or when you want to load independent resources in parallel.

When a resource is created we can call use to run a suspend 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.*

class UserProcessor {
fun start(): Unit = println("Creating UserProcessor")
fun shutdown(): Unit = println("Shutting down UserProcessor")
fun process(ds: DataSource): List<String> =
ds.users().map { "Processed $it" }
}

class DataSource {
fun connect(): Unit = println("Connecting dataSource")
fun users(): List<String> = listOf("User-1", "User-2", "User-3")
fun close(): Unit = println("Closed dataSource")
}

class Service(val db: DataSource, val userProcessor: UserProcessor) {
suspend fun processData(): List<String> = throw RuntimeException("I'm going to leak resources by not closing them")
}

//sampleStart
suspend fun main(): Unit {
val userProcessor = UserProcessor().also { it.start() }
val dataSource = DataSource().also { it.connect() }
val service = Service(dataSource, userProcessor)

service.processData()

dataSource.close()
userProcessor.shutdown()
}
//sampleEnd

In the following example, we are creating and using a service that has a dependency on two resources: A database and a processor. All resources need to be closed in the correct order at the end. However this program is not safe because it is prone to leaking dataSource and userProcessor when an exception or cancellation signal occurs whilst using the service. As a consequence of the resource leak, this program does not guarantee the correct release of resources if something fails while acquiring or using the resource. Additionally manually keeping track of acquisition effects is an unnecessary overhead.

We can split the above program into 3 different steps:

  1. Acquiring the resource

  2. Using the resource

  3. Releasing the resource with either ExitCase.Completed, ExitCase.Failure or ExitCase.Cancelled.

That is exactly what Resource does, and how we can solve our problem:

Constructing Resource

Creating a resource can be easily done by the resource DSL, and there are two ways to define the finalizers with release or releaseCase.

import arrow.fx.coroutines.*

val resourceA = resource {
"A"
} release { a ->
println("Releasing $a")
}

val resourceB = resource {
"B"
} releaseCase { b, exitCase ->
println("Releasing $b with exit: $exitCase")
}

Here releaseCase also signals with what ExitCase state the use step finished.

Using and composing Resource

import arrow.fx.coroutines.*

class UserProcessor {
fun start(): Unit = println("Creating UserProcessor")
fun shutdown(): Unit = println("Shutting down UserProcessor")
fun process(ds: DataSource): List<String> =
ds.users().map { "Processed $it" }
}

class DataSource {
fun connect(): Unit = println("Connecting dataSource")
fun users(): List<String> = listOf("User-1", "User-2", "User-3")
fun close(): Unit = println("Closed dataSource")
}

class Service(val db: DataSource, val userProcessor: UserProcessor) {
suspend fun processData(): List<String> = userProcessor.process(db)
}

//sampleStart
val userProcessor = resource {
UserProcessor().also(UserProcessor::start)
} release UserProcessor::shutdown

val dataSource = resource {
DataSource().also { it.connect() }
} release DataSource::close

suspend fun main(): Unit {
userProcessor.parZip(dataSource) { userProcessor, ds ->
Service(ds, userProcessor)
}.use { service -> service.processData() }
}
//sampleEnd

Resources are immutable and can be composed using zip or parZip. Resources guarantee that their release finalizers are always invoked in the correct order when an exception is raised or the context where the program is running gets canceled.

To achieve this Resource ensures that the acquire&release step are NonCancellable. If a cancellation signal, or an exception is received during acquire, the resource is assumed to not have been acquired and thus will not trigger the release function. => Any composed resources that are already acquired they will be guaranteed to release as expected.

If you don't need a data-type like Resource but want a function alternative to try/catch/finally with automatic error composition, and automatic NonCancellableacquire and release steps use bracketCase or bracket.

Types

Link copied to clipboard
class Allocate<A>(val acquire: suspend () -> A, val release: suspend (A, ExitCase) -> Unit) : Resource<A>
Link copied to clipboard
class Bind<A, B>(val source: Resource<A>, val f: (A) -> Resource<B>) : Resource<B>
Link copied to clipboard
object Companion
Link copied to clipboard
class Defer<A>(val resource: suspend () -> Resource<A>) : Resource<A>
Link copied to clipboard
data class Dsl<A>(val dsl: suspend ResourceScope.() -> A) : Resource<A>

Functions

Link copied to clipboard
fun <B> ap(ff: Resource<(A) -> B>): Resource<B>
Link copied to clipboard
fun <B> flatMap(f: (A) -> Resource<B>): Resource<B>

Create a resource value of B from a resource A by mapping f.

Link copied to clipboard
fun <B> map(f: suspend (A) -> B): Resource<B>
Link copied to clipboard
fun <B, C> parZip(fb: Resource<B>, f: suspend (A, B) -> C): Resource<C>

fun <B, C> parZip(    ctx: CoroutineContext = Dispatchers.Default,     fb: Resource<B>,     f: suspend (A, B) -> C): Resource<C>

Composes two Resources together by zipping them in parallel, by running both their acquire handlers in parallel, and both release handlers in parallel.

Link copied to clipboard
fun tap(f: suspend (A) -> Unit): Resource<A>

Useful for setting up/configuring an acquired resource

Link copied to clipboard
infix suspend tailrec fun <B> use(f: suspend (A) -> B): B

Use the created resource When done will run all finalizers

Link copied to clipboard
fun <B> zip(other: Resource<B>): Resource<Pair<A, B>>
inline fun <B, C> zip(other: Resource<B>, crossinline combine: (A, B) -> C): Resource<C>
inline fun <B, C, D, E> zip(    b: Resource<B>,     c: Resource<C>,     d: Resource<D>,     crossinline map: (A, B, C, D) -> E): Resource<E>
inline fun <B, C, D, E, F, G> zip(    b: Resource<B>,     c: Resource<C>,     d: Resource<D>,     e: Resource<E>,     crossinline map: (A, B, C, D, E) -> G): Resource<G>
inline fun <B, C, D, E, F, G, H> zip(    b: Resource<B>,     c: Resource<C>,     d: Resource<D>,     e: Resource<E>,     f: Resource<F>,     crossinline map: (A, B, C, D, E, F) -> G): Resource<G>
inline 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>,     crossinline map: (A, B, C, D, E, F, G) -> H): Resource<H>
inline 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>,     crossinline map: (A, B, C, D, E, F, G, H) -> I): Resource<I>
inline 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>,     crossinline map: (A, B, C, D, E, F, G, H, I) -> J): Resource<J>
inline 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>,     crossinline map: (A, B, C, D, E, F, G, H, I, J) -> K): Resource<K>

inline fun <B, C, D> zip(    b: Resource<B>,     c: Resource<C>,     crossinline map: (A, B, C) -> D): Resource<D>

Combines two independent resource values with the provided map function, returning the resulting immutable Resource value. The finalizers run in order of left to right by using flatMap under the hood, but zip provides a nicer syntax for combining values that don't depend on each-other.

Inheritors

Link copied to clipboard
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Extensions

Link copied to clipboard
fun <A> Resource<A>.asFlow(): <ERROR CLASS><A>

runs Resource.use and emits A of the resource

Link copied to clipboard
infix fun <A> Resource<A>.release(release: suspend (A) -> Unit): Resource<A>

Composes a release action to a Resource.use action creating a Resource.

Link copied to clipboard
infix fun <A> Resource<A>.releaseCase(release: suspend (A, ExitCase) -> Unit): Resource<A>

Composes a releaseCase action to a Resource.use action creating a Resource.