From b0d38e2e24c163ab83fb57f06a081cb739c42292 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:55:44 +0100 Subject: [PATCH 01/12] Add function bijections --- .../src/main/kotlin/kategory/arrow/FunctionBijections.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt diff --git a/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt new file mode 100644 index 00000000000..53a628f7945 --- /dev/null +++ b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt @@ -0,0 +1,9 @@ +package kategory + +interface FunctionInject { + operator fun

invoke(p: P): R +} + +interface FunctionProject { + operator fun invoke(p: P): R +} \ No newline at end of file From 35ef5b4d7a63bc8513d111cd5b8756556ef0655f Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:56:09 +0100 Subject: [PATCH 02/12] Add Function1 --- .../main/kotlin/kategory/arrow/Function1.kt | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 kategory/src/main/kotlin/kategory/arrow/Function1.kt diff --git a/kategory/src/main/kotlin/kategory/arrow/Function1.kt b/kategory/src/main/kotlin/kategory/arrow/Function1.kt new file mode 100644 index 00000000000..500fabaff18 --- /dev/null +++ b/kategory/src/main/kotlin/kategory/arrow/Function1.kt @@ -0,0 +1,55 @@ +package kategory + +typealias Function1F

= HK + +fun ((P) -> R).k(): Function1 = + Function1(this) + +@Suppress("UNCHECKED_CAST") +fun Function1F.ev(): FunctionInject = + this as FunctionInject + +// We don't we want an inherited class to avoid equivalence issues, so a simple HK wrapper will do +data class Function1(val f: (A) -> R) : FunctionInject, Function1F { + @Suppress("UNCHECKED_CAST") + override operator fun

invoke(p: P): R = + f(p as A) + + class F private constructor() + + companion object { + fun

monad() = object : Function1MonadReader

{} + + fun

monadReader() = object : Function1MonadReader

{} + } +} + +interface Function1MonadReader

: MonadReader { + + override fun ask(): HK = + { a: P -> a }.k() + + override fun local(f: (P) -> P, fa: HK): Function1 = + f.andThen { fa.ev().invoke(it) }.k() + + override fun pure(a: A): Function1 = + { _: P -> a }.k() + + override fun map(fa: HK, f: (A) -> B): HK = + f.compose { b: B -> fa.ev().invoke(b) }.k() + + override fun flatMap(fa: HK, f: (A) -> HK): Function1 = + Function1 { p -> f(fa.ev().invoke(p)).ev().invoke(p) } + + override fun tailRecM(a: A, f: (A) -> HK>): Function1 = + Function1 { p -> + tailrec fun loop(thisA: A): B = + f(thisA).ev().invoke(p).fold({ loop(it) }, { it }) + + loop(a) + } + + fun monad(): Monad = this + + fun monadReader(): MonadReader = this +} \ No newline at end of file From ba17ce9dcac7e38ddb82d9a050e94ef403a5c0c2 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:56:24 +0100 Subject: [PATCH 03/12] Add tests for Function1 --- .../kotlin/kategory/data/Function1Test.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 kategory/src/test/kotlin/kategory/data/Function1Test.kt diff --git a/kategory/src/test/kotlin/kategory/data/Function1Test.kt b/kategory/src/test/kotlin/kategory/data/Function1Test.kt new file mode 100644 index 00000000000..d9d0fa5b3b8 --- /dev/null +++ b/kategory/src/test/kotlin/kategory/data/Function1Test.kt @@ -0,0 +1,25 @@ +package kategory + +import io.kotlintest.KTestJUnitRunner +import io.kotlintest.matchers.shouldBe +import org.junit.runner.RunWith + +@RunWith(KTestJUnitRunner::class) +class Function1Test : UnitSpec() { + init { + testLaws(MonadLaws.laws(Function1.monad(), object : Eq> { + override fun eqv(a: HK, b: HK): Boolean = + a.ev()(1) == b.ev()(1) + })) + + "Function1Monad.binding should for comprehend over all values of multiple Function0" { + val M = Function1.monad() + M.binding { + val x = Function1 { _: Int -> 1 }.bind() + val y = !Function1 { _: Int -> 2 } + val z = bind { Function1 { _: Int -> 3 } } + yields(x + y + z) + }.ev().invoke(0) shouldBe 6 + } + } +} \ No newline at end of file From 4f398fc1e04184263cedd6d34ebe020f2b98d7fd Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:57:03 +0100 Subject: [PATCH 04/12] Remove old unchecked cast suppression --- kategory/src/main/kotlin/kategory/instances/TryInstances.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/kategory/src/main/kotlin/kategory/instances/TryInstances.kt b/kategory/src/main/kotlin/kategory/instances/TryInstances.kt index c4f181509cc..b9065624233 100644 --- a/kategory/src/main/kotlin/kategory/instances/TryInstances.kt +++ b/kategory/src/main/kotlin/kategory/instances/TryInstances.kt @@ -19,7 +19,6 @@ interface TryInstances : override fun handleErrorWith(fa: TryKind, f: (Throwable) -> TryKind): Try = fa.ev().recoverWith { f(it).ev() } - @Suppress("UNCHECKED_CAST") override fun tailRecM(a: A, f: (A) -> TryKind>): Try = f(a).ev().fold({ Try.raiseError(it) }, { either -> either.fold({ tailRecM(it, f) }, { Try.Success(it) }) }) From 02fe432b6eeae995d77a05a41f3b3616e63d743a Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:57:29 +0100 Subject: [PATCH 05/12] Fix Function0 to use final type rather than HK --- .../src/main/kotlin/kategory/arrow/Function0.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kategory/src/main/kotlin/kategory/arrow/Function0.kt b/kategory/src/main/kotlin/kategory/arrow/Function0.kt index 45c1e4d0922..9e9b02055cd 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function0.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function0.kt @@ -1,6 +1,6 @@ package kategory -fun (() -> A).k(): HK = +fun (() -> A).k(): Function0 = Function0(this) fun HK.ev(): () -> A = @@ -13,22 +13,22 @@ data class Function0(internal val f: () -> A) : HK { companion object : Bimonad, GlobalInstance>() { - override fun flatMap(fa: HK, f: (A) -> HK): HK = - f(fa.ev().invoke()) + override fun flatMap(fa: HK, f: (A) -> HK): Function0 = + f(fa.ev().invoke()).ev().k() - override fun coflatMap(fa: HK, f: (HK) -> B): HK = + override fun coflatMap(fa: HK, f: (HK) -> B): Function0 = { f(fa) }.k() - override fun pure(a: A): HK = + override fun pure(a: A): Function0 = { a }.k() override fun extract(fa: HK): A = fa.ev().invoke() - override fun map(fa: HK, f: (A) -> B): HK = + override fun map(fa: HK, f: (A) -> B): Function0 = pure(f(fa.ev().invoke())) - override fun tailRecM(a: A, f: (A) -> HK>): HK = + override fun tailRecM(a: A, f: (A) -> HK>): Function0 = Function0 { tailrec fun loop(thisA: A): B = f(thisA).ev().invoke().fold({ loop(it) }, { it }) From 27dbeefe9b1ad610f7a8ceec2d71cb622aef5273 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 02:57:50 +0100 Subject: [PATCH 06/12] Add free contravariance --- kategory/src/main/kotlin/kategory/data/Composed.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kategory/src/main/kotlin/kategory/data/Composed.kt b/kategory/src/main/kotlin/kategory/data/Composed.kt index 0529c31edad..128e7b79945 100644 --- a/kategory/src/main/kotlin/kategory/data/Composed.kt +++ b/kategory/src/main/kotlin/kategory/data/Composed.kt @@ -13,7 +13,7 @@ fun HK>.lift(): HK, A> = fun HK, A>.lower(): HK> = this as HK> -data class ComposedFoldable(val FF: Foldable, val GF: Foldable) : Foldable> { +data class ComposedFoldable(val FF: Foldable, val GF: Foldable) : Foldable> { override fun foldL(fa: HK, A>, b: B, f: (B, A) -> B): B = FF.foldL(fa.lower(), b, { bb, aa -> GF.foldL(aa, bb, f) }) From ac04952e8a035e0320c651dd8b10f43e8a266037 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 03:05:29 +0100 Subject: [PATCH 07/12] Move instances to a separate file. Add companion methods for functor and applicative. --- .../main/kotlin/kategory/arrow/Function1.kt | 36 +++---------------- .../kategory/instances/Function1Instances.kt | 31 ++++++++++++++++ 2 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 kategory/src/main/kotlin/kategory/instances/Function1Instances.kt diff --git a/kategory/src/main/kotlin/kategory/arrow/Function1.kt b/kategory/src/main/kotlin/kategory/arrow/Function1.kt index 500fabaff18..38b1153314a 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function1.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function1.kt @@ -18,38 +18,12 @@ data class Function1(val f: (A) -> R) : FunctionInject, Function class F private constructor() companion object { - fun

monad() = object : Function1MonadReader

{} + fun

functor() = object : Function1Instances

{} - fun

monadReader() = object : Function1MonadReader

{} - } -} - -interface Function1MonadReader

: MonadReader { - - override fun ask(): HK = - { a: P -> a }.k() - - override fun local(f: (P) -> P, fa: HK): Function1 = - f.andThen { fa.ev().invoke(it) }.k() - - override fun pure(a: A): Function1 = - { _: P -> a }.k() + fun

applicative() = object : Function1Instances

{} - override fun map(fa: HK, f: (A) -> B): HK = - f.compose { b: B -> fa.ev().invoke(b) }.k() + fun

monad() = object : Function1Instances

{} - override fun flatMap(fa: HK, f: (A) -> HK): Function1 = - Function1 { p -> f(fa.ev().invoke(p)).ev().invoke(p) } - - override fun tailRecM(a: A, f: (A) -> HK>): Function1 = - Function1 { p -> - tailrec fun loop(thisA: A): B = - f(thisA).ev().invoke(p).fold({ loop(it) }, { it }) - - loop(a) - } - - fun monad(): Monad = this - - fun monadReader(): MonadReader = this + fun

monadReader() = object : Function1Instances

{} + } } \ No newline at end of file diff --git a/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt b/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt new file mode 100644 index 00000000000..5dc083e3cb7 --- /dev/null +++ b/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt @@ -0,0 +1,31 @@ +package kategory + +interface Function1Instances

: + Functor, + Applicative, + Monad, + MonadReader { + + override fun ask(): HK = + { a: P -> a }.k() + + override fun local(f: (P) -> P, fa: HK): Function1 = + f.andThen { fa.ev().invoke(it) }.k() + + override fun pure(a: A): Function1 = + { _: P -> a }.k() + + override fun map(fa: HK, f: (A) -> B): HK = + f.compose { b: B -> fa.ev().invoke(b) }.k() + + override fun flatMap(fa: HK, f: (A) -> HK): Function1 = + Function1 { p -> f(fa.ev().invoke(p)).ev().invoke(p) } + + override fun tailRecM(a: A, f: (A) -> HK>): Function1 = + Function1 { p -> + tailrec fun loop(thisA: A): B = + f(thisA).ev().invoke(p).fold({ loop(it) }, { it }) + + loop(a) + } +} \ No newline at end of file From 66d56bcf9ccf78bb12bb5fd77eced262a55ea261 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 03:08:33 +0100 Subject: [PATCH 08/12] Rename to sound more mathy --- kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt index 53a628f7945..c8d7afddd85 100644 --- a/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt +++ b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt @@ -4,6 +4,6 @@ interface FunctionInject { operator fun

invoke(p: P): R } -interface FunctionProject { +interface FunctionSurject { operator fun invoke(p: P): R } \ No newline at end of file From b323cc6f139ed589150f7ee59c9c3878c5895262 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 03:10:18 +0100 Subject: [PATCH 09/12] Rename generics to be more accurate --- kategory/src/main/kotlin/kategory/arrow/Function1.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kategory/src/main/kotlin/kategory/arrow/Function1.kt b/kategory/src/main/kotlin/kategory/arrow/Function1.kt index 38b1153314a..76aa3b8b89b 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function1.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function1.kt @@ -1,6 +1,6 @@ package kategory -typealias Function1F

= HK +typealias Function1F = HK fun ((P) -> R).k(): Function1 = Function1(this) From 7fe15ea9c9ec252f6e2f5c2eaf938ec871be1413 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 10:50:22 +0100 Subject: [PATCH 10/12] Fix typo in comment --- kategory/src/main/kotlin/kategory/arrow/Function0.kt | 2 +- kategory/src/main/kotlin/kategory/arrow/Function1.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kategory/src/main/kotlin/kategory/arrow/Function0.kt b/kategory/src/main/kotlin/kategory/arrow/Function0.kt index 9e9b02055cd..db0829b4739 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function0.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function0.kt @@ -6,7 +6,7 @@ fun (() -> A).k(): Function0 = fun HK.ev(): () -> A = (this as Function0).f -// We don't we want an inherited class to avoid equivalence issues, so a simple HK wrapper will do +// We don't want an inherited class to avoid equivalence issues, so a simple HK wrapper will do data class Function0(internal val f: () -> A) : HK { class F private constructor() diff --git a/kategory/src/main/kotlin/kategory/arrow/Function1.kt b/kategory/src/main/kotlin/kategory/arrow/Function1.kt index 76aa3b8b89b..4071435e4b6 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function1.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function1.kt @@ -9,7 +9,7 @@ fun ((P) -> R).k(): Function1 = fun Function1F.ev(): FunctionInject = this as FunctionInject -// We don't we want an inherited class to avoid equivalence issues, so a simple HK wrapper will do +// We don't want an inherited class to avoid equivalence issues, so a simple HK wrapper will do data class Function1(val f: (A) -> R) : FunctionInject, Function1F { @Suppress("UNCHECKED_CAST") override operator fun

invoke(p: P): R = From 197b41005275b1a8151b0b73d49555b52c14e5b1 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 11:04:07 +0100 Subject: [PATCH 11/12] Rename function from invoke to invokeInject and invokeSurject --- kategory/src/main/kotlin/kategory/arrow/Function1.kt | 7 +++++-- .../src/main/kotlin/kategory/arrow/FunctionBijections.kt | 4 ++-- kategory/src/test/kotlin/kategory/data/Function1Test.kt | 8 ++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/kategory/src/main/kotlin/kategory/arrow/Function1.kt b/kategory/src/main/kotlin/kategory/arrow/Function1.kt index 4071435e4b6..ac0df8f3ceb 100644 --- a/kategory/src/main/kotlin/kategory/arrow/Function1.kt +++ b/kategory/src/main/kotlin/kategory/arrow/Function1.kt @@ -11,10 +11,13 @@ fun Function1F.ev(): FunctionInject = // We don't want an inherited class to avoid equivalence issues, so a simple HK wrapper will do data class Function1(val f: (A) -> R) : FunctionInject, Function1F { - @Suppress("UNCHECKED_CAST") - override operator fun

invoke(p: P): R = + override fun

invokeInject(p: P): R = f(p as A) + @Suppress("UNCHECKED_CAST") + operator fun invoke(a: A): R = + f(a) + class F private constructor() companion object { diff --git a/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt index c8d7afddd85..b4a26dd7e62 100644 --- a/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt +++ b/kategory/src/main/kotlin/kategory/arrow/FunctionBijections.kt @@ -1,9 +1,9 @@ package kategory interface FunctionInject { - operator fun

invoke(p: P): R + fun

invokeInject(p: P): R } interface FunctionSurject { - operator fun invoke(p: P): R + fun invokeSurject(p: P): R } \ No newline at end of file diff --git a/kategory/src/test/kotlin/kategory/data/Function1Test.kt b/kategory/src/test/kotlin/kategory/data/Function1Test.kt index d9d0fa5b3b8..fc71c2d3150 100644 --- a/kategory/src/test/kotlin/kategory/data/Function1Test.kt +++ b/kategory/src/test/kotlin/kategory/data/Function1Test.kt @@ -9,9 +9,13 @@ class Function1Test : UnitSpec() { init { testLaws(MonadLaws.laws(Function1.monad(), object : Eq> { override fun eqv(a: HK, b: HK): Boolean = - a.ev()(1) == b.ev()(1) + a.ev().invokeInject(1) == b.ev().invokeInject(1) })) + "bla" { + { a: Int -> a + 1 }.k().invoke(1) + } + "Function1Monad.binding should for comprehend over all values of multiple Function0" { val M = Function1.monad() M.binding { @@ -19,7 +23,7 @@ class Function1Test : UnitSpec() { val y = !Function1 { _: Int -> 2 } val z = bind { Function1 { _: Int -> 3 } } yields(x + y + z) - }.ev().invoke(0) shouldBe 6 + }.ev().invokeInject(0) shouldBe 6 } } } \ No newline at end of file From 552d0c9b7edcf60bb9066110e8729b4d18d32ce3 Mon Sep 17 00:00:00 2001 From: pakoito Date: Fri, 21 Jul 2017 11:17:10 +0100 Subject: [PATCH 12/12] Fix instance for method rename --- .../main/kotlin/kategory/instances/Function1Instances.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt b/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt index 5dc083e3cb7..e6fb0294440 100644 --- a/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt +++ b/kategory/src/main/kotlin/kategory/instances/Function1Instances.kt @@ -10,21 +10,21 @@ interface Function1Instances

: { a: P -> a }.k() override fun local(f: (P) -> P, fa: HK): Function1 = - f.andThen { fa.ev().invoke(it) }.k() + f.andThen { fa.ev().invokeInject(it) }.k() override fun pure(a: A): Function1 = { _: P -> a }.k() override fun map(fa: HK, f: (A) -> B): HK = - f.compose { b: B -> fa.ev().invoke(b) }.k() + f.compose { b: B -> fa.ev().invokeInject(b) }.k() override fun flatMap(fa: HK, f: (A) -> HK): Function1 = - Function1 { p -> f(fa.ev().invoke(p)).ev().invoke(p) } + Function1 { p -> f(fa.ev().invokeInject(p)).ev().invokeInject(p) } override fun tailRecM(a: A, f: (A) -> HK>): Function1 = Function1 { p -> tailrec fun loop(thisA: A): B = - f(thisA).ev().invoke(p).fold({ loop(it) }, { it }) + f(thisA).ev().invokeInject(p).fold({ loop(it) }, { it }) loop(a) }