1
+ package kategory
2
+
3
+ import kategory.effects.data.internal.Platform.onceOnly
4
+ import kotlinx.coroutines.experimental.CoroutineScope
5
+ import kotlinx.coroutines.experimental.CoroutineStart
6
+ import kotlinx.coroutines.experimental.Job
7
+ import kotlinx.coroutines.experimental.launch
8
+ import java.util.concurrent.atomic.AtomicReference
9
+ import kotlin.coroutines.experimental.CoroutineContext
10
+
11
+ @higherkind data class JobKW <out A >(val thunk : ((Either <Throwable , A >) -> Unit ) -> Job ) : JobKWKind<A> {
12
+
13
+ fun <B > map (f : (A ) -> B ): JobKW <B > =
14
+ JobKW { ff: (Either <Throwable , B >) -> Unit ->
15
+ thunk { either: Either <Throwable , A > ->
16
+ ff(either.map(f))
17
+ }
18
+ }
19
+
20
+ fun <B > flatMap (coroutineContext : CoroutineContext , f : (A ) -> JobKW <B >): JobKW <B > =
21
+ JobKW { ff: (Either <Throwable , B >) -> Unit ->
22
+ val result = AtomicReference <Either <Throwable , A >>()
23
+ thunk(result::set).apply {
24
+ invokeOnCompletion { t: Throwable ? ->
25
+ val state: Either <Throwable , A >? = result.get()
26
+ when {
27
+ t == null && state != null -> state.fold({ ff(it.left()) }, { f(it).thunk(ff) })
28
+ t != null -> JobKW .raiseError<B >(coroutineContext, t)
29
+ else -> throw IllegalStateException (" JobKW flatMap completed without success or error" )
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ companion object {
36
+ inline operator fun <A > invoke (coroutineContext : CoroutineContext , noinline f : suspend CoroutineScope .() -> A ): JobKW <A > =
37
+ JobKW {
38
+ onceOnly(it).let { callback: (Either <Throwable , A >) -> Unit ->
39
+ launch(coroutineContext, CoroutineStart .DEFAULT ) {
40
+ callback(
41
+ try {
42
+ f().right()
43
+ } catch (err: Throwable ) {
44
+ err.left()
45
+ }
46
+ )
47
+ }
48
+ }
49
+ }
50
+
51
+ inline fun <A > unsafe (coroutineContext : CoroutineContext , noinline f : suspend CoroutineScope .() -> Either <Throwable , A >): JobKW <A > =
52
+ JobKW {
53
+ onceOnly(it).let { callback: (Either <Throwable , A >) -> Unit ->
54
+ launch(coroutineContext, CoroutineStart .DEFAULT ) {
55
+ callback(f())
56
+ }
57
+ }
58
+ }
59
+
60
+ inline fun <A > async (coroutineContext : CoroutineContext , crossinline fa : Proc <A >): JobKW <A > =
61
+ JobKW {
62
+ onceOnly(it).let { callback: (Either <Throwable , A >) -> Unit ->
63
+ launch(coroutineContext, CoroutineStart .DEFAULT ) {
64
+ fa(callback)
65
+ }
66
+ }
67
+ }
68
+
69
+ inline fun <A > pure (coroutineContext : CoroutineContext , a : A ): JobKW <A > =
70
+ JobKW (coroutineContext) { a }
71
+
72
+ inline fun <A > raiseError (coroutineContext : CoroutineContext , t : Throwable ): JobKW <A > =
73
+ JobKW .unsafe(coroutineContext) { t.left() }
74
+
75
+ fun <A , B > tailRecM (coroutineContext : CoroutineContext , a : A , f : (A ) -> JobKW <Either <A , B >>): JobKW <B > =
76
+ JobKW .async(coroutineContext) { ff: (Either <Throwable , B >) -> Unit ->
77
+ f(a).runJob { either: Either <Throwable , Either <A , B >> ->
78
+ either.fold({ ff(it.left()) }, {
79
+ when (it) {
80
+ is Either .Right -> ff(it.b.right())
81
+ is Either .Left -> tailRecM(coroutineContext, a, f)
82
+ }
83
+ })
84
+ }
85
+ }
86
+
87
+ inline fun instances (coroutineContext : CoroutineContext ): JobKWInstances =
88
+ object : JobKWInstances {
89
+ override fun CC (): CoroutineContext = coroutineContext
90
+ }
91
+
92
+ inline fun functor (coroutineContext : CoroutineContext ): Functor <JobKWHK > = instances(coroutineContext)
93
+
94
+ inline fun applicative (coroutineContext : CoroutineContext ): Applicative <JobKWHK > = instances(coroutineContext)
95
+
96
+ inline fun monad (coroutineContext : CoroutineContext ): Monad <JobKWHK > = instances(coroutineContext)
97
+
98
+ inline fun monadError (coroutineContext : CoroutineContext ): MonadError <JobKWHK , Throwable > = instances(coroutineContext)
99
+
100
+ inline fun asyncContext (coroutineContext : CoroutineContext ): AsyncContext <JobKWHK > = instances(coroutineContext)
101
+ }
102
+ }
103
+
104
+ fun <A > JobKWKind<A>.runJob (ff : (Either <Throwable , A >) -> Unit ): Job =
105
+ this .ev().thunk(ff)
106
+
107
+ inline fun <A > JobKW<A>.handleErrorWith (crossinline function : (Throwable ) -> JobKW <A >): JobKW <A > =
108
+ JobKW { ff: (Either <Throwable , A >) -> Unit ->
109
+ val result = AtomicReference <Either <Throwable , A >>()
110
+ thunk(result::set).apply {
111
+ invokeOnCompletion { t: Throwable ? ->
112
+ val state: Either <Throwable , A >? = result.get()
113
+ when {
114
+ t == null && state != null -> state.fold({ function(it).thunk(ff) }, { ff(it.right()) })
115
+ t != null -> function(t).thunk(ff)
116
+ else -> throw IllegalStateException (" JobKW handleErrorWith completed without success or error" )
117
+ }
118
+ }
119
+ }
120
+ }
0 commit comments