Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract common implementations of Eq #125

Merged
merged 4 commits into from
Jul 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions kategory/src/main/kotlin/kategory/instances/EvalEq.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kategory

class EvalEq : Eq<HK<Eval.F, Int>> {
override fun eqv(a: HK<Eval.F, Int>, b: HK<Eval.F, Int>): Boolean =
a.ev().value() == b.ev().value()
}
11 changes: 11 additions & 0 deletions kategory/src/main/kotlin/kategory/instances/FreeEq.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package kategory

data class FreeEq<in F, in G, in A>(private val interpreter: FunctionK<F, G>, private val MG: Monad<G>) : Eq<HK<FreeF<F>, A>> {
override fun eqv(a: HK<FreeF<F>, A>, b: HK<FreeF<F>, A>): Boolean =
a.ev().foldMap(interpreter, MG) == b.ev().foldMap(interpreter, MG)

companion object {
inline operator fun <F, reified G, A> invoke(interpreter: FunctionK<F, G>, MG: Monad<G> = monad(), dummy: Unit = Unit): FreeEq<F, G, A> =
FreeEq(interpreter, MG)
}
}
6 changes: 6 additions & 0 deletions kategory/src/main/kotlin/kategory/instances/Function0Eq.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kategory

class Function0Eq : Eq<HK<Function0.F, Int>> {
override fun eqv(a: HK<Function0.F, Int>, b: HK<Function0.F, Int>): Boolean =
a.ev()() == b.ev()()
}
6 changes: 6 additions & 0 deletions kategory/src/main/kotlin/kategory/instances/IOEq.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package kategory

class IOEq : Eq<HK<IO.F, Int>> {
override fun eqv(a: HK<IO.F, Int>, b: HK<IO.F, Int>): Boolean =
a.ev().unsafeRunSync() == b.ev().unsafeRunSync()
}
9 changes: 6 additions & 3 deletions kategory/src/main/kotlin/kategory/typeclasses/Eq.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ interface Eq<in F> : Typeclass {
!eqv(a, b)

companion object {
operator fun <F> invoke() = object : Eq<F> {
override fun eqv(a: F, b: F): Boolean =
fun any(): Eq<Any?> =
EqAny()

private class EqAny : Eq<Any?> {
override fun eqv(a: Any?, b: Any?): Boolean =
a == b

override fun neqv(a: F, b: F): Boolean =
override fun neqv(a: Any?, b: Any?): Boolean =
a != b
}
}
Expand Down
4 changes: 2 additions & 2 deletions kategory/src/test/kotlin/kategory/data/EitherTTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.junit.runner.RunWith
class EitherTTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(EitherTMonad<Id.F, Int>(), Eq()))
testLaws(MonadLaws.laws(EitherTMonad<Id.F, Int>(), Eq.any()))

"map should modify value" {
forAll { a: String ->
Expand Down Expand Up @@ -182,7 +182,7 @@ class EitherTTest : UnitSpec() {
forAll(Gen.oneOf(listOf(10000))) { limit: Int ->
val value: EitherT<Id.F, Int, Int> = EitherTMonad<Id.F, Int>(Id).tailRecM(0) { current ->
if (current == limit)
EitherT.left<Id.F, Int, Either<Int, Int>>(current)
EitherT.left(current)
else
EitherT.pure<Id.F, Int, Either<Int, Int>>(Either.Left(current + 1))
}
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/EitherTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import org.junit.runner.RunWith
class EitherTest : UnitSpec() {
init {

testLaws(MonadErrorLaws.laws(EitherMonadError<Throwable>(), Eq()))
testLaws(MonadErrorLaws.laws(EitherMonadError(), Eq.any()))

"map should modify value" {
forAll { a: Int, b: String ->
Expand Down
5 changes: 1 addition & 4 deletions kategory/src/test/kotlin/kategory/data/EvalTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import org.junit.runner.RunWith
class EvalTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(Eval, object : Eq<HK<Eval.F, Int>> {
override fun eqv(a: HK<Eval.F, Int>, b: HK<Eval.F, Int>): Boolean =
a.ev().value() == b.ev().value()
}))
testLaws(MonadLaws.laws(Eval, EvalEq()))

"should map wrapped value" {
val sideEffect = SideEffect()
Expand Down
5 changes: 1 addition & 4 deletions kategory/src/test/kotlin/kategory/data/Function0Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import org.junit.runner.RunWith
class Function0Test : UnitSpec() {
init {

testLaws(MonadLaws.laws(Function0, object : Eq<HK<Function0.F, Int>> {
override fun eqv(a: HK<Function0.F, Int>, b: HK<Function0.F, Int>): Boolean =
a.ev()() == b.ev()()
}))
testLaws(MonadLaws.laws(Function0, Function0Eq()))

"Function0Monad.binding should for comprehend over all values of multiple Function0" {
Function0.binding {
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/IdTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.junit.runner.RunWith
class IdTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(Id, Eq()))
testLaws(MonadLaws.laws(Id, Eq.any()))

"IdMonad.binding should for comprehend over all values of multiple Ids" {
Id.binding {
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/IorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class IorTest : UnitSpec() {

val intIorMonad = IorMonad(IntMonoid)

testLaws(MonadLaws.laws(intIorMonad, Eq()))
testLaws(MonadLaws.laws(intIorMonad, Eq.any()))

"flatMap() should modify entity" {
forAll { a: Int, b: String ->
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/NonEmptyListTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.runner.RunWith
class NonEmptyListTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(NonEmptyList, Eq()))
testLaws(MonadLaws.laws(NonEmptyList, Eq.any()))

"map should modify values" {
NonEmptyList.of(14).map { it * 3 } shouldBe NonEmptyList.of(42)
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/OptionTTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.runner.RunWith
class OptionTTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(OptionTMonad(NonEmptyList), Eq()))
testLaws(MonadLaws.laws(OptionTMonad(NonEmptyList), Eq.any()))

"map should modify value" {
forAll { a: String ->
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/OptionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class OptionTest: UnitSpec() {

init {

testLaws(MonadLaws.laws(Option, Eq()))
testLaws(MonadLaws.laws(Option, Eq.any()))

"map should modify value" {
Some(12).map { "flower" } shouldBe Some("flower")
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/TryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class TryTest : UnitSpec() {

init {

testLaws(MonadErrorLaws.laws(Try, Eq()))
testLaws(MonadErrorLaws.laws(Try, Eq.any()))

"invoke of any should be success" {
Try.invoke { 1 } shouldBe Success(1)
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/ValidatedTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ValidatedTest : UnitSpec() {
override fun combine(a: String, b: String): String = "$a $b"
}

testLaws(ApplicativeLaws.laws(ValidatedApplicativeError(concatStringSG), Eq()))
testLaws(ApplicativeLaws.laws(ValidatedApplicativeError(concatStringSG), Eq.any()))

"fold should call function on Invalid" {
val exception = Exception("My Exception")
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/data/WriterTTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.junit.runner.RunWith
class WriterTTest : UnitSpec() {
init {

testLaws(MonadLaws.laws(WriterTMonad(NonEmptyList, IntMonoid), Eq()))
testLaws(MonadLaws.laws(WriterTMonad(NonEmptyList, IntMonoid), Eq.any()))

"tell should accumulate write" {
forAll { a: Int ->
Expand Down
5 changes: 1 addition & 4 deletions kategory/src/test/kotlin/kategory/effects/IOTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ class IOTest : UnitSpec() {

init {

testLaws(MonadLaws.laws(IO, object : Eq<HK<IO.F, Int>> {
override fun eqv(a: HK<IO.F, Int>, b: HK<IO.F, Int>): Boolean =
a.ev().unsafeRunSync() == b.ev().unsafeRunSync()
}))
testLaws(MonadLaws.laws(IO, IOEq()))

"should defer evaluation until run" {
var run = false
Expand Down
11 changes: 4 additions & 7 deletions kategory/src/test/kotlin/kategory/free/FreeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,21 @@ fun <A> HK<Ops.F, A>.ev(): Ops<A> = this as Ops<A>
@RunWith(KTestJUnitRunner::class)
class FreeTest : UnitSpec() {

val program = Ops.binding {
private val program = Ops.binding {
val added = !Ops.add(10, 10)
val subtracted = !Ops.subtract(added, 50)
yields(subtracted)
}.ev()

fun stackSafeTestProgram(n: Int, stopAt: Int): Free<Ops.F, Int> = Ops.binding {
private fun stackSafeTestProgram(n: Int, stopAt: Int): Free<Ops.F, Int> = Ops.binding {
val v = !Ops.add(n, 1)
val r = !if (v < stopAt) stackSafeTestProgram(v, stopAt) else Free.pure<Ops.F, Int>(v)
val r = !if (v < stopAt) stackSafeTestProgram(v, stopAt) else Free.pure(v)
yields(r)
}.ev()

init {

testLaws(MonadLaws.laws(Ops, object : Eq<HK<FreeF<Ops.F>, Int>> {
override fun eqv(a: HK<FreeF<Ops.F>, Int>, b: HK<FreeF<Ops.F>, Int>): Boolean =
a.ev().foldMap(idInterpreter, Id) == b.ev().foldMap(idInterpreter, Id)
}))
testLaws(MonadLaws.laws(Ops, FreeEq(idInterpreter)))

"Can interpret an ADT as Free operations" {
program.foldMap(optionInterpreter, Option).ev() shouldBe Option.Some(-30)
Expand Down
4 changes: 2 additions & 2 deletions kategory/src/test/kotlin/kategory/laws/FunctorLaws.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ object FunctorLaws {
Law("Functor: Covariant Composition", { covariantComposition(AP, EQ) })
)

inline fun <reified F> covariantIdentity(AP: Applicative<F> = applicative<F>(), EQ: Eq<HK<F, Int>> = Eq()): Unit =
inline fun <reified F> covariantIdentity(AP: Applicative<F> = applicative<F>(), EQ: Eq<HK<F, Int>> = Eq.any()): Unit =
forAll(genApplicative(Gen.int(), AP), { fa: HK<F, Int> ->
AP.map(fa, ::identity).equalUnderTheLaw(fa, EQ)
})

inline fun <reified F> covariantComposition(AP: Applicative<F> = applicative<F>(), EQ: Eq<HK<F, Int>> = Eq()): Unit =
inline fun <reified F> covariantComposition(AP: Applicative<F> = applicative<F>(), EQ: Eq<HK<F, Int>> = Eq.any()): Unit =
forAll(
genApplicative(Gen.int(), AP),
genFunctionAToB<Int, Int>(Gen.int()),
Expand Down
2 changes: 1 addition & 1 deletion kategory/src/test/kotlin/kategory/laws/Law.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ package kategory

data class Law(val name: String, val test: () -> Unit)

inline fun <reified A> A.equalUnderTheLaw(b: A, eq: Eq<A> = Eq()): Boolean =
inline fun <reified A> A.equalUnderTheLaw(b: A, eq: Eq<A> = Eq.any()): Boolean =
eq.eqv(this, b)