Skip to content

Commit 9c81e7a

Browse files
authored
Merge pull request #250 from kategory/paco-effectsfix
Remove dependency on kotlinx.coroutines for effects
2 parents b9bc7d8 + 365a6ac commit 9c81e7a

File tree

35 files changed

+120
-406
lines changed

35 files changed

+120
-406
lines changed

kategory-core/build.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
dependencies {
22
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVersion"
3-
compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinxCoroutinesVersion"
43
compile project(':kategory-annotations')
54
kapt project(':kategory-annotations-processor')
65
kaptTest project(':kategory-annotations-processor')

kategory-core/src/main/kotlin/kategory/data/Option.kt

+5-3
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,15 @@ sealed class Option<out A> : OptionKind<A> {
4949

5050
/**
5151
* Returns true if the option is [None], false otherwise.
52-
* Used only for performance instead of fold.
52+
* @note Used only for performance instead of fold.
5353
*/
5454
internal abstract val isEmpty: Boolean
5555

5656
/**
5757
* Returns true if the option is an instance of $some, false otherwise.
58+
* @note Used only for performance instead of fold.
5859
*/
59-
val isDefined: Boolean = !isEmpty
60+
internal val isDefined: Boolean = !isEmpty
6061

6162
/**
6263
* Returns a $some containing the result of applying $f to this $option's
@@ -142,8 +143,9 @@ sealed class Option<out A> : OptionKind<A> {
142143
/**
143144
* Returns false if the option is $none, true otherwise.
144145
* @note Implemented here to avoid the implicit conversion to Iterable.
146+
* @note Used only for performance instead of fold.
145147
*/
146-
val nonEmpty = isDefined
148+
internal val nonEmpty = isDefined
147149

148150
/**
149151
* Returns true if this option is nonempty '''and''' the predicate

kategory-core/src/main/kotlin/kategory/typeclasses/Comonad.kt

+3-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package kategory
22

33
import java.io.Serializable
4-
import kotlin.coroutines.experimental.Continuation
5-
import kotlin.coroutines.experimental.EmptyCoroutineContext
6-
import kotlin.coroutines.experimental.RestrictsSuspension
4+
import kotlin.coroutines.experimental.*
75
import kotlin.coroutines.experimental.intrinsics.COROUTINE_SUSPENDED
86
import kotlin.coroutines.experimental.intrinsics.suspendCoroutineOrReturn
9-
import kotlin.coroutines.experimental.startCoroutine
107

118
/**
129
* The dual of monads, used to extract values from F
@@ -27,9 +24,7 @@ inline fun <reified F, A> HK<F, A>.extract(FT: Comonad<F> = comonad()): A = FT.e
2724
inline fun <reified F, A> HK<F, A>.duplicate(FT: Comonad<F> = comonad()): HK<F, HK<F, A>> = FT.duplicate(this)
2825

2926
@RestrictsSuspension
30-
open class ComonadContinuation<F, A : Any>(val CM: Comonad<F>) : Serializable, Continuation<A> {
31-
32-
override val context = EmptyCoroutineContext
27+
open class ComonadContinuation<F, A : Any>(val CM: Comonad<F>, override val context: CoroutineContext = EmptyCoroutineContext) : Serializable, Continuation<A> {
3328

3429
override fun resume(value: A) {
3530
returnedMonad = value
@@ -59,7 +54,7 @@ open class ComonadContinuation<F, A : Any>(val CM: Comonad<F>) : Serializable, C
5954
* A coroutine is initiated and inside `MonadContinuation` suspended yielding to `flatMap` once all the flatMap binds are completed
6055
* the underlying monad is returned from the act of executing the coroutine
6156
*/
62-
fun <F, B : Any> Comonad<F>.cobinding(c: suspend ComonadContinuation<F, *>.() -> B): B {
57+
fun <F, B : Any> Comonad<F>.cobinding(coroutineContext: CoroutineContext = EmptyCoroutineContext, c: suspend ComonadContinuation<F, *>.() -> B): B {
6358
val continuation = ComonadContinuation<F, B>(this)
6459
c.startCoroutine(continuation, continuation)
6560
return continuation.returnedMonad

kategory-core/src/main/kotlin/kategory/typeclasses/Eq.kt

+5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ interface Eq<in F> : Typeclass {
66
fun neqv(a: F, b: F): Boolean = !eqv(a, b)
77

88
companion object {
9+
inline operator fun <F> invoke(crossinline feqv: (F, F) -> Boolean): Eq<F> = object : Eq<F> {
10+
override fun eqv(a: F, b: F): Boolean =
11+
feqv(a, b)
12+
}
13+
914
fun any(): Eq<Any?> = EqAny
1015

1116
object EqAny : Eq<Any?> {

kategory-core/src/main/kotlin/kategory/typeclasses/Monad.kt

-23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package kategory
22

3-
import kotlinx.coroutines.experimental.runBlocking
43
import java.io.Serializable
54
import kotlin.coroutines.experimental.Continuation
65
import kotlin.coroutines.experimental.CoroutineContext
@@ -67,17 +66,6 @@ open class MonadContinuation<F, A>(M: Monad<F>, override val context: CoroutineC
6766
COROUTINE_SUSPENDED
6867
}
6968

70-
suspend fun <B> bindInContext(coroutineContext: CoroutineContext, m: suspend () -> HK<F, B>): B = suspendCoroutineOrReturn { c ->
71-
val labelHere = c.stackLabels // save the whole coroutine stack labels
72-
val result = runBlocking(coroutineContext) { m() }
73-
returnedMonad = flatMap(result, { x: B ->
74-
c.stackLabels = labelHere
75-
c.resume(x)
76-
returnedMonad
77-
})
78-
COROUTINE_SUSPENDED
79-
}
80-
8169
infix fun <B> yields(b: B): HK<F, B> = yields { b }
8270

8371
infix fun <B> yields(b: () -> B): HK<F, B> = pure(b())
@@ -125,17 +113,6 @@ open class StackSafeMonadContinuation<F, A>(M: Monad<F>, override val context: C
125113
COROUTINE_SUSPENDED
126114
}
127115

128-
suspend fun <B> bindInContext(coroutineContext: CoroutineContext, m: suspend () -> Free<F, B>): B = suspendCoroutineOrReturn { c ->
129-
val labelHere = c.stackLabels // save the whole coroutine stack labels
130-
val freeResult = runBlocking(coroutineContext) { m() }
131-
returnedMonad = freeResult.flatMap { z ->
132-
c.stackLabels = labelHere
133-
c.resume(z)
134-
returnedMonad
135-
}
136-
COROUTINE_SUSPENDED
137-
}
138-
139116
infix fun <B> yields(b: B): Free<F, B> = yields { b }
140117

141118
infix fun <B> yields(b: () -> B): Free<F, B> = Free.liftF(pure(b()))

kategory-core/src/test/kotlin/kategory/data/CoproductTest.kt

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ package kategory
22

33
import io.kotlintest.KTestJUnitRunner
44
import io.kotlintest.matchers.shouldNotBe
5-
import io.kotlintest.properties.forAll
65
import org.junit.runner.RunWith
76

87
@RunWith(KTestJUnitRunner::class)
98
class CoproductTest : UnitSpec() {
10-
val EQ: Eq<HK3<CoproductHK, IdHK, IdHK, Int>> = object : Eq<HK3<CoproductHK, IdHK, IdHK, Int>> {
11-
override fun eqv(a: CoproductKind<IdHK, IdHK, Int>, b: CoproductKind<IdHK, IdHK, Int>): Boolean =
9+
val EQ: Eq<HK3<CoproductHK, IdHK, IdHK, Int>> = Eq { a, b ->
1210
a.ev().extract() == b.ev().extract()
1311
}
1412

kategory-core/src/test/kotlin/kategory/data/EitherTest.kt

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package kategory
22

33
import io.kotlintest.KTestJUnitRunner
4-
import io.kotlintest.matchers.shouldBe
54
import io.kotlintest.matchers.shouldNotBe
65
import io.kotlintest.properties.forAll
76
import kategory.Either.Left
@@ -10,6 +9,10 @@ import org.junit.runner.RunWith
109

1110
@RunWith(KTestJUnitRunner::class)
1211
class EitherTest : UnitSpec() {
12+
val EQ: Eq<HK<EitherKindPartial<IdHK>, Int>> = Eq { a, b ->
13+
a.ev() == b.ev()
14+
}
15+
1316
init {
1417

1518
"instances can be resolved implicitly" {
@@ -24,13 +27,7 @@ class EitherTest : UnitSpec() {
2427

2528
testLaws(MonadErrorLaws.laws(Either.monadError(), Eq.any(), Eq.any()))
2629
testLaws(TraverseLaws.laws(Either.traverse<Throwable>(), Either.applicative(), { it.right() }, Eq.any()))
27-
testLaws(SemigroupKLaws.laws(
28-
Either.semigroupK(),
29-
Either.applicative(),
30-
object : Eq<HK<EitherKindPartial<IdHK>, Int>> {
31-
override fun eqv(a: HK<EitherKindPartial<IdHK>, Int>, b: HK<EitherKindPartial<IdHK>, Int>): Boolean =
32-
a.ev() == b.ev()
33-
}))
30+
testLaws(SemigroupKLaws.laws(Either.semigroupK(), Either.applicative(), EQ))
3431

3532
"getOrElse should return value" {
3633
forAll { a: Int, b: Int ->

kategory-core/src/test/kotlin/kategory/data/EvalTest.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import org.junit.runner.RunWith
88
class EvalTest : UnitSpec() {
99
init {
1010

11-
testLaws(MonadLaws.laws(Eval.monad(), object : Eq<HK<EvalHK, Int>> {
12-
override fun eqv(a: HK<EvalHK, Int>, b: HK<EvalHK, Int>): Boolean =
11+
testLaws(MonadLaws.laws(Eval.monad(), Eq { a, b ->
1312
a.ev().value() == b.ev().value()
1413
}))
1514

kategory-core/src/test/kotlin/kategory/data/Function0Test.kt

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package kategory
22

33
import io.kotlintest.KTestJUnitRunner
4-
import io.kotlintest.matchers.shouldBe
54
import org.junit.runner.RunWith
65

76
@RunWith(KTestJUnitRunner::class)
87
class Function0Test : UnitSpec() {
9-
val EQ: Eq<HK<Function0HK, Int>> = object : Eq<HK<Function0HK, Int>> {
10-
override fun eqv(a: HK<Function0HK, Int>, b: HK<Function0HK, Int>): Boolean =
11-
a() == b()
8+
val EQ: Eq<HK<Function0HK, Int>> = Eq { a, b ->
9+
a() == b()
1210
}
1311

1412
init {
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package kategory
22

33
import io.kotlintest.KTestJUnitRunner
4-
import io.kotlintest.matchers.shouldBe
54
import io.kotlintest.matchers.shouldNotBe
65
import org.junit.runner.RunWith
76

87
@RunWith(KTestJUnitRunner::class)
98
class Function1Test : UnitSpec() {
9+
val EQ: Eq<Function1Kind<Int, Int>> = Eq { a, b ->
10+
a(1) == b(1)
11+
}
12+
1013
init {
1114

1215
"instances can be resolved implicitly" {
@@ -16,9 +19,6 @@ class Function1Test : UnitSpec() {
1619
monadReader<Function1KindPartial<Int>, Int>() shouldNotBe null
1720
}
1821

19-
testLaws(MonadLaws.laws(Function1.monad<Int>(), object : Eq<Function1Kind<Int, Int>> {
20-
override fun eqv(a: Function1Kind<Int, Int>, b: Function1Kind<Int, Int>): Boolean =
21-
a(1) == b(1)
22-
}))
22+
testLaws(MonadLaws.laws(Function1.monad<Int>(), EQ))
2323
}
2424
}

kategory-core/src/test/kotlin/kategory/data/KleisliTest.kt

+2-6
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,8 @@ import org.junit.runner.RunWith
77

88
@RunWith(KTestJUnitRunner::class)
99
class KleisliTest : UnitSpec() {
10-
private fun <A> EQ(): Eq<KleisliKind<TryHK, Int, A>> {
11-
return object : Eq<KleisliKind<TryHK, Int, A>> {
12-
override fun eqv(a: KleisliKind<TryHK, Int, A>, b: KleisliKind<TryHK, Int, A>): Boolean =
13-
a.ev().run(1) == b.ev().run(1)
14-
15-
}
10+
private fun <A> EQ(): Eq<KleisliKind<TryHK, Int, A>> = Eq { a, b ->
11+
a.ev().run(1) == b.ev().run(1)
1612
}
1713

1814
init {

kategory-core/src/test/kotlin/kategory/data/OptionTTest.kt

+13-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import org.junit.runner.RunWith
77

88
@RunWith(KTestJUnitRunner::class)
99
class OptionTTest : UnitSpec() {
10+
val EQ_ID: Eq<HK<OptionTKindPartial<IdHK>, Int>> = Eq { a, b ->
11+
a.value() == b.value()
12+
}
13+
14+
val NELM: Monad<NonEmptyListHK> = monad<NonEmptyListHK>()
15+
1016
init {
1117

1218
"instances can be resolved implicitly" {
@@ -20,51 +26,44 @@ class OptionTTest : UnitSpec() {
2026
functorFilter<OptionTKindPartial<ListKWHK>>() shouldNotBe null
2127
}
2228

23-
val OptionTFIdEq = object : Eq<HK<OptionTKindPartial<IdHK>, Int>> {
24-
override fun eqv(a: HK<OptionTKindPartial<IdHK>, Int>, b: HK<OptionTKindPartial<IdHK>, Int>): Boolean =
25-
a.ev().value == b.ev().value
26-
}
27-
2829
testLaws(MonadLaws.laws(OptionT.monad(NonEmptyList.monad()), Eq.any()))
2930
testLaws(TraverseLaws.laws(OptionT.traverse(), OptionT.applicative(Id.monad()), { OptionT(Id(it.some())) }, Eq.any()))
3031
testLaws(SemigroupKLaws.laws(
3132
OptionT.semigroupK(Id.monad()),
3233
OptionT.applicative(Id.monad()),
33-
OptionTFIdEq))
34+
EQ_ID))
3435

3536
testLaws(MonoidKLaws.laws(
3637
OptionT.monoidK(Id.monad()),
3738
OptionT.applicative(Id.monad()),
38-
OptionTFIdEq))
39+
EQ_ID))
3940

4041
testLaws(FunctorFilterLaws.laws(
4142
OptionT.functorFilter(),
4243
{ OptionT(Id(it.some())) },
43-
OptionTFIdEq))
44-
45-
val nelMonad = monad<NonEmptyListHK>()
44+
EQ_ID))
4645

4746
"toLeft for Some should build a correct EitherT" {
4847
forAll { a: Int, b: String ->
49-
OptionT.fromOption<NonEmptyListHK, Int>(Option.Some(a)).toLeft({ b }, nelMonad) == EitherT.left<NonEmptyListHK, Int, String>(a, applicative())
48+
OptionT.fromOption<NonEmptyListHK, Int>(Option.Some(a)).toLeft({ b }, NELM) == EitherT.left<NonEmptyListHK, Int, String>(a, applicative())
5049
}
5150
}
5251

5352
"toLeft for None should build a correct EitherT" {
5453
forAll { a: Int, b: String ->
55-
OptionT.fromOption<NonEmptyListHK, Int>(Option.None).toLeft({ b }, nelMonad) == EitherT.right<NonEmptyListHK, Int, String>(b, applicative())
54+
OptionT.fromOption<NonEmptyListHK, Int>(Option.None).toLeft({ b }, NELM) == EitherT.right<NonEmptyListHK, Int, String>(b, applicative())
5655
}
5756
}
5857

5958
"toRight for Some should build a correct EitherT" {
6059
forAll { a: Int, b: String ->
61-
OptionT.fromOption<NonEmptyListHK, String>(Option.Some(b)).toRight({ a }, nelMonad) == EitherT.right<NonEmptyListHK, Int, String>(b, applicative())
60+
OptionT.fromOption<NonEmptyListHK, String>(Option.Some(b)).toRight({ a }, NELM) == EitherT.right<NonEmptyListHK, Int, String>(b, applicative())
6261
}
6362
}
6463

6564
"toRight for None should build a correct EitherT" {
6665
forAll { a: Int, b: String ->
67-
OptionT.fromOption<NonEmptyListHK, String>(Option.None).toRight({ a }, nelMonad) == EitherT.left<NonEmptyListHK, Int, String>(a, applicative())
66+
OptionT.fromOption<NonEmptyListHK, String>(Option.None).toRight({ a }, NELM) == EitherT.left<NonEmptyListHK, Int, String>(a, applicative())
6867
}
6968
}
7069

kategory-core/src/test/kotlin/kategory/data/OptionTest.kt

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ class OptionTest : UnitSpec() {
2424
monadError<OptionHK, Unit>() shouldNotBe null
2525
}
2626

27-
val EQ_EITHER: Eq<HK<OptionHK, Either<Unit, Int>>> = object : Eq<HK<OptionHK, Either<Unit, Int>>> {
28-
override fun eqv(a: HK<OptionHK, Either<Unit, Int>>, b: HK<OptionHK, Either<Unit, Int>>): Boolean =
27+
val EQ_EITHER: Eq<HK<OptionHK, Either<Unit, Int>>> = Eq { a, b ->
2928
a.ev().fold(
3029
{ b.ev().fold({ true }, { false }) },
3130
{ eitherA: Either<Unit, Int> ->

kategory-core/src/test/kotlin/kategory/data/StateTTests.kt

+17-23
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ import org.junit.runner.RunWith
77
@RunWith(KTestJUnitRunner::class)
88
class StateTTests : UnitSpec() {
99

10+
val M: StateTMonadStateInstance<TryHK, Int> = StateT.monadState<TryHK, Int>(Try.monad())
11+
12+
val EQ: Eq<StateTKind<TryHK, Int, Int>> = Eq { a, b ->
13+
a.runM(1, Try.monad()) == b.runM(1, Try.monad())
14+
}
15+
16+
val EQ_UNIT: Eq<StateTKind<TryHK, Int, Unit>> = Eq { a, b ->
17+
a.runM(1, Try.monad()) == b.runM(1, Try.monad())
18+
}
19+
20+
val EQ_LIST: Eq<HK<StateTKindPartial<ListKWHK, Int>, Int>> = Eq { a, b ->
21+
a.runM(1, ListKW.monad()) == b.runM(1, ListKW.monad())
22+
}
23+
1024
init {
1125

1226
"instances can be resolved implicitly" {
@@ -17,35 +31,15 @@ class StateTTests : UnitSpec() {
1731
semigroupK<StateTKindPartial<NonEmptyListHK, NonEmptyListHK>>() shouldNotBe null
1832
}
1933

20-
val m: StateTMonadStateInstance<TryHK, Int> = StateT.monadState<TryHK, Int>(Try.monad())
21-
22-
testLaws(MonadStateLaws.laws(
23-
m,
24-
object : Eq<StateTKind<TryHK, Int, Int>> {
25-
override fun eqv(a: StateTKind<TryHK, Int, Int>, b: StateTKind<TryHK, Int, Int>): Boolean =
26-
a.runM(1, Try.monad()) == b.runM(1, Try.monad())
27-
28-
},
29-
object : Eq<StateTKind<TryHK, Int, Unit>> {
30-
override fun eqv(a: StateTKind<TryHK, Int, Unit>, b: StateTKind<TryHK, Int, Unit>): Boolean =
31-
a.runM(1, Try.monad()) == b.runM(1, Try.monad())
32-
}))
33-
34+
testLaws(MonadStateLaws.laws(M, EQ, EQ_UNIT))
3435
testLaws(SemigroupKLaws.laws(
3536
StateT.semigroupK<ListKWHK, Int>(ListKW.monad(), ListKW.semigroupK()),
3637
StateT.applicative<ListKWHK, Int>(ListKW.monad()),
37-
object : Eq<HK<StateTKindPartial<ListKWHK, Int>, Int>> {
38-
override fun eqv(a: HK<StateTKindPartial<ListKWHK, Int>, Int>, b: HK<StateTKindPartial<ListKWHK, Int>, Int>): Boolean =
39-
a.runM(1, ListKW.monad()) == b.runM(1, ListKW.monad())
40-
}))
41-
38+
EQ_LIST))
4239
testLaws(MonadCombineLaws.laws(StateT.monadCombine<ListKWHK, Int>(ListKW.monadCombine()),
4340
{ StateT.lift(ListKW.pure(it), ListKW.monad()) },
4441
{ StateT.lift(ListKW.pure({ s: Int -> s * 2 }), ListKW.monad()) },
45-
object : Eq<HK<StateTKindPartial<ListKWHK, Int>, Int>> {
46-
override fun eqv(a: HK<StateTKindPartial<ListKWHK, Int>, Int>, b: HK<StateTKindPartial<ListKWHK, Int>, Int>): Boolean =
47-
a.runM(1, ListKW.monad()) == b.runM(1, ListKW.monad())
48-
}))
42+
EQ_LIST))
4943

5044
}
5145
}

0 commit comments

Comments
 (0)