-
Notifications
You must be signed in to change notification settings - Fork 451
/
Copy pathFold.kt
122 lines (112 loc) · 3.93 KB
/
Fold.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
@file:JvmMultifileClass
@file:JvmName("Effect")
@file:OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class)
package arrow.core.raise
import arrow.core.nonFatalOrThrow
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.InvocationKind.AT_MOST_ONCE
import kotlin.contracts.InvocationKind.EXACTLY_ONCE
import kotlin.contracts.contract
import kotlin.coroutines.cancellation.CancellationException
import kotlin.experimental.ExperimentalTypeInference
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
/**
* `invoke` the [Effect] and [fold] the result:
* - _success_ [transform] result of [A] to a value of [B].
* - _raised_ [recover] from `raised` value of [R] to a value of [B].
* - _exception_ [error] from [Throwable] by transforming value into [B].
*
* This method should never be wrapped in `try`/`catch` as it will not throw any unexpected errors,
* it will only result in [CancellationException], or fatal exceptions such as `OutOfMemoryError`.
*/
public suspend fun <R, A, B> Effect<R, A>.fold(
error: suspend (error: Throwable) -> B,
recover: suspend (raised: R) -> B,
transform: suspend (value: A) -> B,
): B {
contract {
callsInPlace(error, AT_MOST_ONCE)
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
return fold({ invoke() }, { error(it) }, { recover(it) }, { transform(it) })
}
public suspend fun <R, A, B> Effect<R, A>.fold(
recover: suspend (raised: R) -> B,
transform: suspend (value: A) -> B,
): B {
contract {
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
return fold({ throw it }, recover, transform)
}
public inline fun <R, A, B> EagerEffect<R, A>.fold(
error: (error: Throwable) -> B,
recover: (raised: R) -> B,
transform: (value: A) -> B,
): B {
contract {
callsInPlace(error, AT_MOST_ONCE)
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
return fold({ invoke(this) }, error, recover, transform)
}
public inline fun <R, A, B> EagerEffect<R, A>.fold(recover: (R) -> B, transform: (A) -> B): B {
contract {
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
return fold({ throw it }, recover, transform)
}
@JvmName("_foldOrThrow")
public inline fun <R, A, B> fold(
@BuilderInference program: Raise<R>.() -> A,
recover: (raised: R) -> B,
transform: (value: A) -> B,
): B {
contract {
callsInPlace(program, EXACTLY_ONCE)
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
return fold(program, { throw it }, recover, transform)
}
@JvmName("_fold")
public inline fun <R, A, B> fold(
@BuilderInference program: Raise<R>.() -> A,
error: (error: Throwable) -> B,
recover: (raised: R) -> B,
transform: (value: A) -> B,
): B {
contract {
callsInPlace(program, EXACTLY_ONCE)
callsInPlace(error, AT_MOST_ONCE)
callsInPlace(recover, AT_MOST_ONCE)
callsInPlace(transform, AT_MOST_ONCE)
}
val raise = DefaultRaise()
return try {
transform(program(raise))
} catch (e: CancellationException) {
recover(e.raisedOrRethrow(raise))
} catch (e: Throwable) {
error(e.nonFatalOrThrow())
}
}
/** Returns the raised value, rethrows the CancellationException if not our scope */
@PublishedApi
internal fun <R> CancellationException.raisedOrRethrow(raise: DefaultRaise): R =
if (this is RaiseCancellationException && this.raise === raise) _raised as R
else throw this
/** Serves as both purposes of a scope-reference token, and a default implementation for Raise. */
@PublishedApi
internal class DefaultRaise : Raise<Any?> {
override fun raise(r: Any?): Nothing = throw RaiseCancellationException(r, this)
}
/** CancellationException is required to cancel coroutines when raising from within them. */
private class RaiseCancellationException(val _raised: Any?, val raise: Raise<Any?>) :
CancellationException("Raised Continuation")