This repository has been archived by the owner on Feb 24, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
/
MonadError.kt
153 lines (144 loc) · 5.14 KB
/
MonadError.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package arrow.typeclasses
import arrow.Kind
import arrow.core.Either
import arrow.core.NonFatal
import arrow.documented
import kotlin.coroutines.startCoroutine
/**
* ank_macro_hierarchy(arrow.typeclasses.MonadError)
*/
interface MonadError<F, E> : ApplicativeError<F, E>, Monad<F> {
fun <A> Kind<F, A>.ensure(error: () -> E, predicate: (A) -> Boolean): Kind<F, A> =
this.flatMap {
if (predicate(it)) just(it)
else raiseError(error())
}
fun <A, B> Kind<F, A>.redeemWith(fe: (E) -> Kind<F, B>, fb: (A) -> Kind<F, B>): Kind<F, B> =
flatMap(fb).handleErrorWith(fe)
fun <A> Kind<F, Either<E, A>>.rethrow(): Kind<F, A> =
flatMap { it.fold({ e -> raiseError<A>(e) }, { a -> just(a) }) }
}
/**
* ank_macro_hierarchy(arrow.typeclasses.MonadThrow)
*
* MonadThrow has the error type fixed to Throwable. It provides [fx.monadThrow] for automatically catching throwable
* errors in the context of a binding, short-circuiting the complete computation and returning the error raised to the
* same computational context (through [raiseError]).
*
* ```kotlin:ank:playground:extension
* _imports_
*
* fun main(args: Array<String>) {
* val result =
* //sampleStart
* _extensionFactory_
* //sampleEnd
* println(result)
* }
* ```
*
* ### Example
*
* Oftentimes we find ourselves in situations where we need to sequence some computations that could potentially fail.
* [fx.monadThrow] allows us to safely compute those by automatically catching any exceptions thrown during the process.
*
* ```kotlin:ank:playground:extension
* _imports_
* import arrow.Kind
* import arrow.typeclasses.MonadThrow
*
* typealias Impacted = Boolean
*
* object Lettuce
* object Knife
* object Salad
* class InsufficientAmount(val quantityInGrams : Int) : Throwable("You need $quantityInGrams more grams of ingredient")
*
* fun <F> MonadThrow<F>.takeFoodFromRefrigerator(): Kind<F, Lettuce> = just(Lettuce)
* fun <F> MonadThrow<F>.getKnife(): Kind<F, Knife> = just(Knife)
* fun <F> MonadThrow<F>.launchImpure(tool: Knife, ingredient: Lettuce): Salad {
* throw InsufficientAmount(5)
* }
*
* fun main(args: Array<String>) {
* //sampleStart
* fun <F> MonadThrow<F>.prepareLunch(): Kind<F, Salad> =
* fx.monadThrow {
* val lettuce = takeFoodFromRefrigerator()()
* val knife = getKnife()()
* val salad = launchImpure(knife, lettuce) // this throws!
* salad
* }
*
* val result = _extensionFactory_.prepareLunch()
* //sampleEnd
* println(result)
* }
* ```
*/
@documented
interface MonadThrow<F> : MonadError<F, Throwable> {
/**
* Entry point for monad bindings which enables for comprehensions. The underlying implementation is based on
* coroutines. A coroutine is initiated and suspended inside [MonadThrowContinuation] yielding to [Monad.flatMap].
* Once all the flatMap binds are completed, the underlying monad is returned from the act of executing the coroutine.
*
* This one operates over [MonadError] instances that can support [Throwable] in their error type automatically
* lifting errors as failed computations in their monadic context and not letting exceptions thrown as the regular
* monad binding does.
*
* ### Example
*
* Oftentimes we find ourselves in situations where we need to sequence some computations that could potentially fail.
* [fx.monadThrow] allows us to safely compute those by automatically catching any exceptions thrown during the process.
*
* ```kotlin:ank:playground:extension
* _imports_
* import arrow.Kind
* import arrow.typeclasses.MonadThrow
*
* typealias SaladPrepared = Boolean
*
* object Lettuce
* object Knife
* class InsufficientAmount(val quantityInGrams : Int) : Throwable("You need $quantityInGrams more grams of ingredient")
*
* fun <F> MonadThrow<F>.takeFoodFromRefrigerator(): Kind<F, Lettuce> = just(Lettuce)
* fun <F> MonadThrow<F>.getKnife(): Kind<F, Knife> = just(Knife)
* fun <F> MonadThrow<F>.launchImpure(tool: Knife, ingredient: Lettuce): Salad {
* throw InsufficientAmount(5)
* }
*
* fun main(args: Array<String>) {
* //sampleStart
* fun <F> MonadThrow<F>.prepareLunch(): Kind<F, SaladPrepared> =
* fx.monadThrow {
* val lettuce = takeFoodFromRefrigerator()()
* val knife = getKnife()()
* val salad = launchImpure(knife, lettuce) // this throws!
* salad
* }
*
* val result = _extensionFactory_.prepareLunch()
* //sampleEnd
* println(result)
* }
* ```
*
*/
override val fx: MonadThrowFx<F>
get() = object : MonadThrowFx<F> {
override val M: MonadThrow<F> = this@MonadThrow
}
fun <A> Throwable.raiseNonFatal(): Kind<F, A> =
if (NonFatal(this)) raiseError(this) else throw this
}
interface MonadThrowFx<F> : MonadFx<F> {
override val M: MonadThrow<F>
fun <A> monadThrow(c: suspend MonadThrowSyntax<F>.() -> A): Kind<F, A> {
val continuation = MonadThrowContinuation<F, A>(M)
val wrapReturn: suspend MonadThrowSyntax<F>.() -> Kind<F, A> = { just(c()) }
wrapReturn.startCoroutine(continuation, continuation)
return continuation.returnedMonad()
}
}