Youssef Shoaib [MOD]
11/30/2023, 10:27 PMtransform
lambda for top-level fold for raise. I think perhaps top-level fold shouldn't exist, instead being replaced by recover
, which describes the intent much more clearly. I've elaborated further in this PR
Regardless, putting this into actual code, I tried implementing this against Arrow 2, but it's failing a test that seems to suggest an interesting design choice of Effect. More info in thread...Youssef Shoaib [MOD]
11/30/2023, 10:34 PMpublic suspend fun <Error, A, B> Effect<Error, A>.fold(
catch: suspend (throwable: Throwable) -> B,
recover: suspend (error: Error) -> B,
transform: suspend (value: A) -> B,
): B {
return recover({ transform(invoke()) }, { recover(it) }, { catch(it) })
}
This passes all the tests, except one, which reveals an interesting implementation detail of top-level fold that I didn't consider before.
The failing test is:
@Test fun shiftLeakedResultsInRaiseLeakException() = runTest {
effect {
suspend { raise("failure") }
}.fold(
{
it.message shouldStartWith "raise or bind was called outside of its DSL scope"
},
{ unreachable() }) { f -> f() }
}
This fails because, without a pure transformation parameter being supported in top-level fold and recover, the transformation always happens before the raise scope is finished, and hence our effect actually executes like:
effect {
suspend { raise("failure") }.invoke()
}
To put this into code, top-level fold in the happy path currently does:
val res = block(raise)
raise.complete()
transform(res)
where it completes the raise before it transforms the result. Without a transformation lambda, top level fold can only do:
block(raise).also { raise.complete() }
thus executing any transformation before the raise is completedYoussef Shoaib [MOD]
11/30/2023, 10:40 PM