//arrow-fx-coroutines/arrow.fx.coroutines/Resource

Resource

common 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](../-exit-case/-completed/index.html), [ExitCase.Failure](../-exit-case/-failure/index.html) or [ExitCase.Cancelled](../-exit-case/-cancelled/index.html).

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

Name Summary
Allocate common class Allocate<A>(acquire: suspend () -> A, release: suspend (A, ExitCase) -> Unit) : Resource<A>
Bind common class Bind<A, B>(source: Resource<A>, f: (A) -> Resource<B>) : Resource<B>
Companion common object Companion
Defer common class Defer<A>(resource: suspend () -> Resource<A>) : Resource<A>

Functions

Name Summary
ap common fun <B> ap(ff: Resource<(A) -> B>): Resource<B>
flatMap common fun <B> flatMap(f: (A) -> Resource<B>): Resource<B>
Create a resource value of B from a resource A by mapping f.
map common fun <B> map(f: suspend (A) -> B): Resource<B>
parZip common fun <B, C> parZip(fb: Resource<B>, f: suspend (A, B) -> C): Resource<C>
common 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.
tap common fun <B> tap(f: suspend (A) -> Unit): Resource<A>
Useful for setting up/configuring an acquired resource
use common infix suspend tailrec fun <B> use(f: suspend (A) -> B): B
Use the created resource When done will run all finalizers
zip common 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>
common 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

Name
Resource
Resource
Resource

Extensions

Name Summary
release common infix fun <A> Resource<A>.release(release: suspend (A) -> Unit): Resource<A>
Composes a release action to a Resource.use action creating a Resource.
releaseCase common 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.

Do you like Arrow?

Arrow Org
<