From dd2ec57f28c2c22c3290bdcb66a483ccb0a0a183 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 27 Jun 2022 20:56:56 +0200 Subject: [PATCH 1/5] Fix arity-4, and improve docs --- .../api/arrow-fx-coroutines.api | 1 + .../kotlin/arrow/fx/coroutines/Resource.kt | 153 +++++++++--------- .../arrow/fx/coroutines/ResourceTest.kt | 24 +-- 3 files changed, 93 insertions(+), 85 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api index 16a583788cd..65cfab63d29 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api +++ b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api @@ -372,6 +372,7 @@ public final class arrow/fx/coroutines/ResourceKt { public static final fun releaseCase-zgiIeyo (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function3;)Larrow/fx/coroutines/Resource; public static final fun resource (Lkotlin/jvm/functions/Function1;)Lkotlin/jvm/functions/Function1; public static final fun sequence (Ljava/lang/Iterable;)Larrow/fx/coroutines/Resource; + public static final fun traverse (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/fx/coroutines/Resource; public static final fun traverseResource (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Larrow/fx/coroutines/Resource; } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt index 5140cd1d784..d792f17dd3d 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt @@ -2,13 +2,8 @@ package arrow.fx.coroutines import arrow.core.continuations.AtomicRef import arrow.core.continuations.update -import arrow.core.NonEmptyList -import arrow.core.ValidatedNel import arrow.core.identity -import arrow.core.invalidNel import arrow.core.prependTo -import arrow.core.traverse -import arrow.core.valid import arrow.fx.coroutines.continuations.ResourceScope import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CancellationException @@ -17,6 +12,9 @@ import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext +import kotlin.experimental.ExperimentalTypeInference +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads /** * [Resource] models resource allocation and releasing. It is especially useful when multiple resources that depend on each other @@ -48,7 +46,6 @@ import kotlinx.coroutines.withContext * suspend fun processData(): List = 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() } @@ -59,11 +56,10 @@ import kotlinx.coroutines.withContext * 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. + * However, this program is not safe because it is prone to leak `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: @@ -99,6 +95,8 @@ import kotlinx.coroutines.withContext * * # Using and composing Resource * + * Arrow offers the same elegant `bind` DSL for Resource as you might be familiar with from Arrow Core. + * * ```kotlin * import arrow.fx.coroutines.* * @@ -119,7 +117,6 @@ import kotlinx.coroutines.withContext * suspend fun processData(): List = userProcessor.process(db) * } * - * //sampleStart * val userProcessor = resource { * UserProcessor().also(UserProcessor::start) * } release UserProcessor::shutdown @@ -129,11 +126,12 @@ import kotlinx.coroutines.withContext * } release DataSource::close * * suspend fun main(): Unit { - * userProcessor.parZip(dataSource) { userProcessor, ds -> + * resource { + * parZip({ userProcessor.bind() }, { dataSource.bind() }) { userProcessor, ds -> * Service(ds, userProcessor) - * }.use { service -> service.processData() } + * } + * }.use { service -> service.processData() } * } - * //sampleEnd * ``` * * @@ -163,7 +161,6 @@ public sealed class Resource { * } * * suspend fun main(): Unit { - * //sampleStart * val dataSource = resource { * DataSource().also { it.connect() } * } release DataSource::close @@ -171,7 +168,6 @@ public sealed class Resource { * val res = dataSource * .use { ds -> "Using data source: ${ds.users()}" } * .also(::println) - * //sampleEnd * } * ``` * @@ -196,12 +192,14 @@ public sealed class Resource { } b } + is Allocate -> bracketCase(acquire, f, release) is Bind<*, *> -> Dsl { val any = source.bind() val ff = this@Resource.f as (Any?) -> Resource ff(any).bind() }.use(f) + is Defer -> resource().use(f) } @@ -240,7 +238,6 @@ public sealed class Resource { * } * * suspend fun main(): Unit { - * //sampleStart * val dataSource = resource { * DataSource().also { it.connect() } * } release DataSource::close @@ -252,7 +249,6 @@ public sealed class Resource { * * dataSource.flatMap(::database) * .use { println("Using database which uses dataSource") } - * //sampleEnd * } * ``` * @@ -301,7 +297,6 @@ public sealed class Resource { * suspend fun processData(): List = userProcessor.process(db) * } * - * //sampleStart * val userProcessor = resource { * UserProcessor().also(UserProcessor::start) * } release UserProcessor::shutdown @@ -315,7 +310,6 @@ public sealed class Resource { * Service(ds, userProcessor) * }.use { service -> service.processData() } * } - * //sampleEnd * ``` * * @@ -325,7 +319,7 @@ public sealed class Resource { public inline fun zip( b: Resource, c: Resource, - crossinline map: (A, B, C) -> D + crossinline map: (A, B, C) -> D, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind()) @@ -335,18 +329,18 @@ public sealed class Resource { b: Resource, c: Resource, d: Resource, - crossinline map: (A, B, C, D) -> E + crossinline map: (A, B, C, D) -> E, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind()) } - public inline fun zip( + public inline fun zip( b: Resource, c: Resource, d: Resource, e: Resource, - crossinline map: (A, B, C, D, E) -> G + crossinline map: (A, B, C, D, E) -> G, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind()) @@ -358,7 +352,7 @@ public sealed class Resource { d: Resource, e: Resource, f: Resource, - crossinline map: (A, B, C, D, E, F) -> G + crossinline map: (A, B, C, D, E, F) -> G, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind(), f.bind()) @@ -371,7 +365,7 @@ public sealed class Resource { e: Resource, f: Resource, g: Resource, - crossinline map: (A, B, C, D, E, F, G) -> H + crossinline map: (A, B, C, D, E, F, G) -> H, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind()) @@ -385,7 +379,7 @@ public sealed class Resource { f: Resource, g: Resource, h: Resource, - crossinline map: (A, B, C, D, E, F, G, H) -> I + crossinline map: (A, B, C, D, E, F, G, H) -> I, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind()) @@ -400,7 +394,7 @@ public sealed class Resource { g: Resource, h: Resource, i: Resource, - crossinline map: (A, B, C, D, E, F, G, H, I) -> J + crossinline map: (A, B, C, D, E, F, G, H, I) -> J, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind()) @@ -416,7 +410,7 @@ public sealed class Resource { h: Resource, i: Resource, j: Resource, - crossinline map: (A, B, C, D, E, F, G, H, I, J) -> K + crossinline map: (A, B, C, D, E, F, G, H, I, J) -> K, ): Resource = arrow.fx.coroutines.continuations.resource { map(bind(), b.bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind(), j.bind()) @@ -452,7 +446,6 @@ public sealed class Resource { * suspend fun processData(): List = userProcessor.process(db) * } * - * //sampleStart * val userProcessor = resource { * UserProcessor().also { it.start() } * } release UserProcessor::shutdown @@ -466,14 +459,13 @@ public sealed class Resource { * Service(ds, userProcessor) * }.use { service -> service.processData() } * } - * //sampleEnd * ``` * */ public fun parZip( ctx: CoroutineContext = Dispatchers.Default, fb: Resource, - f: suspend (A, B) -> C + f: suspend (A, B) -> C, ): Resource = arrow.fx.coroutines.continuations.resource { parZip(ctx, { this@Resource.bind() }, { fb.bind() }) { a, b -> f(a, b) } @@ -490,7 +482,7 @@ public sealed class Resource { public class Allocate( public val acquire: suspend () -> A, - public val release: suspend (A, ExitCase) -> Unit + public val release: suspend (A, ExitCase) -> Unit, ) : Resource() @Deprecated( @@ -507,6 +499,7 @@ public sealed class Resource { public companion object { @PublishedApi + @Deprecated("This will be removed from the binary in Arrow 2.0", level = DeprecationLevel.ERROR) internal val unit: Resource = just(Unit) /** @@ -519,19 +512,17 @@ public sealed class Resource { * suspend fun releaseResource(r: Int, exitCase: ExitCase): Unit = println("Releasing expensive resource: $r, exit: $exitCase") * * suspend fun main(): Unit { - * //sampleStart * val resource = Resource(::acquireResource, ::releaseResource) * resource.use { * println("Expensive resource under use! $it") * } - * //sampleEnd * } * ``` * */ public operator fun invoke( acquire: suspend () -> A, - release: suspend (A, ExitCase) -> Unit + release: suspend (A, ExitCase) -> Unit, ): Resource = Allocate(acquire, release) /** @@ -630,8 +621,16 @@ public infix fun Resource.releaseCase(release: suspend (A, ExitCase) -> U Resource({ a }, { _, ex -> release(a, ex) }).bind() } +@Deprecated("traverseResource is being renamed to traverse to simplify the Arrow API", ReplaceWith("traverse(f)")) +public inline fun Iterable.traverseResource(crossinline f: (A) -> Resource): Resource> = + arrow.fx.coroutines.continuations.resource { + map { a -> + f(a).bind() + } + } + /** - * Traverse this [Iterable] and collects the resulting `Resource` of [f] into a `Resource>`. + * Traverse this [Iterable] and collects the resulting `Resource` of [transform] into a `Resource>`. * * ```kotlin * import arrow.fx.coroutines.* @@ -647,12 +646,11 @@ public infix fun Resource.releaseCase(release: suspend (A, ExitCase) -> U * suspend fun fileToString(file: File): String = file.toString() * * suspend fun main(): Unit { - * //sampleStart * val res: List = listOf( * "data.json", * "user.json", * "resource.json" - * ).traverseResource { uri -> + * ).traverse { uri -> * resource { * openFile(uri) * } release { file -> @@ -660,23 +658,23 @@ public infix fun Resource.releaseCase(release: suspend (A, ExitCase) -> U * } * }.use { files -> * files.map { fileToString(it) } - * } - * //sampleEnd - * res.forEach(::println) + * }.forEach(::println) * } * ``` * */ -public inline fun Iterable.traverseResource(crossinline f: (A) -> Resource): Resource> = +@OptIn(ExperimentalTypeInference::class) +@OverloadResolutionByLambdaReturnType +public inline fun Iterable.traverse(crossinline transform: (A) -> Resource): Resource> = arrow.fx.coroutines.continuations.resource { map { a -> - f(a).bind() + transform(a).bind() } } /** * Sequences this [Iterable] of [Resource]s. - * [Iterable.map] and [sequence] is equivalent to [traverseResource]. + * [Iterable.map] and [sequence] is equivalent to [traverse]. * * ```kotlin * import arrow.fx.coroutines.* @@ -692,7 +690,6 @@ public inline fun Iterable.traverseResource(crossinline f: (A) -> Reso * suspend fun fileToString(file: File): String = file.toString() * * suspend fun main(): Unit { - * //sampleStart * val res: List = listOf( * "data.json", * "user.json", @@ -705,16 +702,42 @@ public inline fun Iterable.traverseResource(crossinline f: (A) -> Reso * } * }.sequence().use { files -> * files.map { fileToString(it) } - * } - * //sampleEnd - * res.forEach(::println) + * }.forEach(::println) * } * ``` * */ @Suppress("NOTHING_TO_INLINE") public inline fun Iterable>.sequence(): Resource> = - traverseResource(::identity) + traverse { it } + +/** + * Runs [Resource.use] and emits [A] of the resource + * + * ```kotlin + * import arrow.fx.coroutines.* + * + * fun Flow.writeAll(path: Path): Flow = + * Resource.fromCloseable { path.toFile().outputStream() } + * .asFlow() + * .flatMapConcat { writer -> byteFlow.map { writer.write(it) } } + * .flowOn(Dispatchers.IO) + * + * fun Path.readAll(): Flow = flow { + * path.useLines { lines -> emitAll(lines) } + * } + * + * Path("example.kt") + * .readAll() + * . + * ``` + */ +public fun Resource.asFlow(): Flow = + flow { + use { + emit(it) + } + } private class ResourceScopeImpl : ResourceScope { val finalizers: AtomicRef Unit>> = AtomicRef(emptyList()) @@ -735,6 +758,7 @@ private class ResourceScopeImpl : ResourceScope { Platform.composeErrors(e, e2)?.let { throw it } } }) + is Resource.Bind<*, *> -> { val dsl: suspend ResourceScope.() -> A = { val any = source.bind() @@ -743,34 +767,17 @@ private class ResourceScopeImpl : ResourceScope { } dsl(this@ResourceScopeImpl) } + is Resource.Defer -> resource().bind() } } -// Version that doesn't rethrow `CancellationException` because we need to run all finalizers regardless of CancellationException -private inline fun catchNel(f: () -> A): ValidatedNel = - try { - f().valid() - } catch (e: Throwable) { - e.invalidNel() - } - private suspend fun List Unit>.cancelAll( exitCase: ExitCase, - first: Throwable? = null -): Throwable? = traverse { f -> - catchNel { f(exitCase) } -}.fold({ - if (first != null) Platform.composeErrors(NonEmptyList(first, it)) - else Platform.composeErrors(it) -}, { first }) - -/** - * runs [Resource.use] and emits [A] of the resource - */ -public fun Resource.asFlow(): Flow = - flow { - use { - emit(it) - } - } + first: Throwable? = null, +): Throwable? = fold(first) { acc, finalizer -> + val other = kotlin.runCatching { finalizer(exitCase) }.exceptionOrNull() + other?.let { + acc?.apply { addSuppressed(other) } ?: other + } ?: acc +} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ResourceTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ResourceTest.kt index 9e46b749b71..f68e052ed3d 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ResourceTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ResourceTest.kt @@ -41,7 +41,7 @@ class ResourceTest : ArrowFxSpec( r(a).flatMap { r(it + b) } .use { it + 1 } shouldBe (a + b) + 1 - l.shouldContainExactly(a, a + b, - a - b, -a) + l.shouldContainExactly(a, a + b, -a - b, -a) } } @@ -90,39 +90,39 @@ class ResourceTest : ArrowFxSpec( Arb.list(Arb.int()), Arb.functionAToB(Arb.string()) ) { list, f -> - list.traverseResource { Resource.just(f(it)) } resourceShouldBe Resource.just(list.map(f)) + list.traverse { Resource.just(f(it)) } resourceShouldBe Resource.just(list.map(f)) } } - "traverseResource: map + sequence == traverse" { + "traverse: map + sequence == traverse" { checkAll( Arb.list(Arb.int()), Arb.string().map { { _: Int -> Resource.just(it) } } ) { list, f -> - list.traverseResource(f) resourceShouldBe list.map(f).sequence() + list.traverse(f) resourceShouldBe list.map(f).sequence() } } - "traverseResource: parallelComposition" { + "traverse: parallelComposition" { checkAll( Arb.list(Arb.int()), Arb.functionAToB(Arb.string()), Arb.functionAToB(Arb.string()) ) { list, f, g -> - val ff = list.traverseResource { Resource.just(f(it)) } - val gg = list.traverseResource { Resource.just(g(it)) } + val ff = list.traverse { Resource.just(f(it)) } + val gg = list.traverse { Resource.just(g(it)) } val result = ff.zip(gg).map { (a, b) -> a.zip(b) } - list.traverseResource { Resource.just(f(it) to g(it)) } resourceShouldBe result + list.traverse { Resource.just(f(it) to g(it)) } resourceShouldBe result } } - "traverseResource: leftToRight" { + "traverse: leftToRight" { checkAll(Arb.list(Arb.int())) { list -> - list.traverseResource { Resource.just(it) } + list.traverse { Resource.just(it) } .use(::identity) shouldBe list } } @@ -154,8 +154,8 @@ class ResourceTest : ArrowFxSpec( Resource({ CheckableAutoClose() }) { a, _ -> a.close() } "parZip - success" { - val all = (1..depth).traverseResource { closeable() }.parZip( - (1..depth).traverseResource { closeable() } + val all = (1..depth).traverse { closeable() }.parZip( + (1..depth).traverse { closeable() } ) { a, b -> a + b }.use { all -> all.also { all.forEach { it.started shouldBe true } } } From f0a40723a576750055045602b18efa6a0f93ccbd Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 27 Jun 2022 21:00:34 +0200 Subject: [PATCH 2/5] Remove unused imports --- .../src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt index d792f17dd3d..51312d7c208 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt @@ -13,8 +13,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext import kotlin.experimental.ExperimentalTypeInference -import kotlin.jvm.JvmName -import kotlin.jvm.JvmOverloads /** * [Resource] models resource allocation and releasing. It is especially useful when multiple resources that depend on each other @@ -709,7 +707,7 @@ public inline fun Iterable.traverse(crossinline transform: (A) -> Reso */ @Suppress("NOTHING_TO_INLINE") public inline fun Iterable>.sequence(): Resource> = - traverse { it } + traverse(::identity) /** * Runs [Resource.use] and emits [A] of the resource From 2dab9799694c57497debddab7026ad8ed78e2042 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 4 Jul 2022 11:39:05 +0200 Subject: [PATCH 3/5] Rename to f for consistency --- .../src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt index 51312d7c208..7e5af3f005a 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt @@ -628,7 +628,7 @@ public inline fun Iterable.traverseResource(crossinline f: (A) -> Reso } /** - * Traverse this [Iterable] and collects the resulting `Resource` of [transform] into a `Resource>`. + * Traverse this [Iterable] and collects the resulting `Resource` of [f] into a `Resource>`. * * ```kotlin * import arrow.fx.coroutines.* @@ -663,10 +663,10 @@ public inline fun Iterable.traverseResource(crossinline f: (A) -> Reso */ @OptIn(ExperimentalTypeInference::class) @OverloadResolutionByLambdaReturnType -public inline fun Iterable.traverse(crossinline transform: (A) -> Resource): Resource> = +public inline fun Iterable.traverse(crossinline f: (A) -> Resource): Resource> = arrow.fx.coroutines.continuations.resource { map { a -> - transform(a).bind() + f(a).bind() } } From 1388ae1f950e4cebd716bb5f735cd6802ecfb636 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Mon, 4 Jul 2022 11:55:21 +0200 Subject: [PATCH 4/5] Update knit snippets --- .../src/jvmTest/kotlin/examples/example-resource-03.kt | 6 ++++-- .../src/jvmTest/kotlin/examples/example-resource-04.kt | 2 -- .../src/jvmTest/kotlin/examples/example-resource-05.kt | 2 -- .../src/jvmTest/kotlin/examples/example-resource-08.kt | 2 -- .../src/jvmTest/kotlin/examples/example-resource-10.kt | 7 ++----- .../src/jvmTest/kotlin/examples/example-resource-11.kt | 5 +---- 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt index 389c744072b..7c74e63cbae 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt @@ -29,7 +29,9 @@ val dataSource = resource { } release DataSource::close suspend fun main(): Unit { - userProcessor.parZip(dataSource) { userProcessor, ds -> + resource { + parZip({ userProcessor.bind() }, { dataSource.bind() }) { userProcessor, ds -> Service(ds, userProcessor) - }.use { service -> service.processData() } + } + }.use { service -> service.processData() } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-04.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-04.kt index f9cfe3cc95e..f3f67d497cc 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-04.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-04.kt @@ -10,7 +10,6 @@ class DataSource { } suspend fun main(): Unit { - //sampleStart val dataSource = resource { DataSource().also { it.connect() } } release DataSource::close @@ -18,5 +17,4 @@ suspend fun main(): Unit { val res = dataSource .use { ds -> "Using data source: ${ds.users()}" } .also(::println) - //sampleEnd } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-05.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-05.kt index 588bf83ea37..24d8787a74b 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-05.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-05.kt @@ -16,7 +16,6 @@ class Database(private val database: DataSource) { } suspend fun main(): Unit { - //sampleStart val dataSource = resource { DataSource().also { it.connect() } } release DataSource::close @@ -28,5 +27,4 @@ suspend fun main(): Unit { dataSource.flatMap(::database) .use { println("Using database which uses dataSource") } - //sampleEnd } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-08.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-08.kt index 0c1964bb4bb..b1743db3bdf 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-08.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-08.kt @@ -7,10 +7,8 @@ suspend fun acquireResource(): Int = 42.also { println("Getting expensive resour suspend fun releaseResource(r: Int, exitCase: ExitCase): Unit = println("Releasing expensive resource: $r, exit: $exitCase") suspend fun main(): Unit { - //sampleStart val resource = Resource(::acquireResource, ::releaseResource) resource.use { println("Expensive resource under use! $it") } - //sampleEnd } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt index 1430c7ef2b7..15b101818ed 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt @@ -14,12 +14,11 @@ suspend fun closeFile(file: File): Unit = file.close() suspend fun fileToString(file: File): String = file.toString() suspend fun main(): Unit { - //sampleStart val res: List = listOf( "data.json", "user.json", "resource.json" - ).traverseResource { uri -> + ).traverse { uri -> resource { openFile(uri) } release { file -> @@ -27,7 +26,5 @@ suspend fun main(): Unit { } }.use { files -> files.map { fileToString(it) } - } - //sampleEnd - res.forEach(::println) + }.forEach(::println) } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt index 8bc906f38b3..ca00dd913a4 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt @@ -14,7 +14,6 @@ suspend fun closeFile(file: File): Unit = file.close() suspend fun fileToString(file: File): String = file.toString() suspend fun main(): Unit { - //sampleStart val res: List = listOf( "data.json", "user.json", @@ -27,7 +26,5 @@ suspend fun main(): Unit { } }.sequence().use { files -> files.map { fileToString(it) } - } - //sampleEnd - res.forEach(::println) + }.forEach(::println) } From 0b0a13c70a45f05ca52c88595a84aedb379a8940 Mon Sep 17 00:00:00 2001 From: Simon Vergauwen Date: Wed, 13 Jul 2022 11:11:54 +0200 Subject: [PATCH 5/5] Fix docs --- .../src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt | 7 +++++-- .../src/jvmTest/kotlin/examples/example-resource-03.kt | 1 + .../src/jvmTest/kotlin/examples/example-resource-10.kt | 3 ++- .../src/jvmTest/kotlin/examples/example-resource-11.kt | 3 ++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt index 7e5af3f005a..8fef5fbe0b6 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt @@ -97,6 +97,7 @@ import kotlin.experimental.ExperimentalTypeInference * * ```kotlin * import arrow.fx.coroutines.* + * import arrow.fx.coroutines.continuations.resource * * class UserProcessor { * fun start(): Unit = println("Creating UserProcessor") @@ -656,7 +657,8 @@ public inline fun Iterable.traverseResource(crossinline f: (A) -> Reso * } * }.use { files -> * files.map { fileToString(it) } - * }.forEach(::println) + * } + * res.forEach(::println) * } * ``` * @@ -700,7 +702,8 @@ public inline fun Iterable.traverse(crossinline f: (A) -> Resource) * } * }.sequence().use { files -> * files.map { fileToString(it) } - * }.forEach(::println) + * } + * res.forEach(::println) * } * ``` * diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt index 7c74e63cbae..d369bbbf7fd 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-03.kt @@ -2,6 +2,7 @@ package arrow.fx.coroutines.examples.exampleResource03 import arrow.fx.coroutines.* +import arrow.fx.coroutines.continuations.resource class UserProcessor { fun start(): Unit = println("Creating UserProcessor") diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt index 15b101818ed..57e567884b4 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-10.kt @@ -26,5 +26,6 @@ suspend fun main(): Unit { } }.use { files -> files.map { fileToString(it) } - }.forEach(::println) + } + res.forEach(::println) } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt index ca00dd913a4..3987d148522 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-resource-11.kt @@ -26,5 +26,6 @@ suspend fun main(): Unit { } }.sequence().use { files -> files.map { fileToString(it) } - }.forEach(::println) + } + res.forEach(::println) }