From 608f28dd4216b5dc28f91980ce43ec6223863827 Mon Sep 17 00:00:00 2001 From: Alberto Ballano Date: Thu, 11 Mar 2021 15:36:24 +0100 Subject: [PATCH 1/3] Update Semigroup, Semiring and Monoid docs Make Semigroup's maybeCombine to use nullables instead of Option --- .../kotlin/arrow/typeclasses/Semigroup.kt | 5 +- .../main/kotlin/arrow/typeclasses/Semiring.kt | 37 +++------ .../docs/arrow/typeclasses/monoid/README.md | 79 +++++++++++-------- .../arrow/typeclasses/semigroup/README.md | 39 +++++---- 4 files changed, 84 insertions(+), 76 deletions(-) diff --git a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semigroup.kt b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semigroup.kt index d58263255f5..7dd21a89e74 100644 --- a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semigroup.kt +++ b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semigroup.kt @@ -1,7 +1,5 @@ package arrow.typeclasses -import arrow.core.Option - interface Semigroup { /** * Combine two [A] values. @@ -11,7 +9,8 @@ interface Semigroup { operator fun A.plus(b: A): A = this.combine(b) - fun A.maybeCombine(b: A?): A = Option.fromNullable(b).fold({ this }, { combine(it) }) + fun A.maybeCombine(b: A?): A = + b?.let { combine(it) } ?: this companion object } diff --git a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt index 0d452f4db8e..5f5cd11a379 100644 --- a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt +++ b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt @@ -11,11 +11,11 @@ import arrow.documented * (a.combineMultiplicate(b)).combineMultiplicate(c) == a.combineMultiplicate(b.combineMultiplicate(c)) * ``` * - * The [one] value serves exactly like the [empty] function for an additive [Monoid], just adapted for the multiplicative + * The [one] function serves exactly like the [empty] function for an additive [Monoid], just adapted for the multiplicative * version. This forms the following law: * * ```kotlin - * combineMultiplicate(x, one) == combineMultiplicate(one, x) == x + * a.combineMultiplicate(one()) == one().combineMultiplicate(a) == a * ``` * * Please note that the empty function has been renamed to [zero] to get a consistent naming style inside the semiring. @@ -27,24 +27,26 @@ import arrow.documented * Here a some examples: * * ```kotlin:ank:playground - * import arrow.core.extensions.* + * import arrow.core.int + * import arrow.typeclasses.Semiring * * fun main(args: Array) { * val result = * //sampleStart - * Int.semiring().run { 1.combine(2) } + * Semiring.int().run { 1.combine(2) } * //sampleEnd * println(result) * } * ``` * * ```kotlin:ank:playground - * import arrow.core.extensions.* + * import arrow.core.int + * import arrow.typeclasses.Semiring * * fun main(args: Array) { * val result = * //sampleStart - * Int.semiring().run { 2.combineMultiplicate(3) } + * Semiring.int().run { 2.combineMultiplicate(3) } * //sampleEnd * println(result) * } @@ -53,29 +55,14 @@ import arrow.documented * The type class `Semiring` also has support for the `+` `*` syntax: * * ```kotlin:ank:playground - * import arrow.core.Option - * import arrow.core.extensions.* + * import arrow.core.int + * import arrow.typeclasses.Semiring * * fun main(args: Array) { * val result = * //sampleStart - * Int.semiring().run { - * 1 + 2 - * } - * //sampleEnd - * println(result) - * } - * ``` - * - * ```kotlin:ank:playground - * import arrow.core.Option - * import arrow.core.extensions.* - * - * fun main(args: Array) { - * val result = - * //sampleStart - * Int.semiring().run { - * 2 * 3 + * Semiring.int().run { + * 1 + 2 * 3 * } * //sampleEnd * println(result) diff --git a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md index bfba649ca92..2dd149ed596 100644 --- a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md +++ b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md @@ -9,37 +9,39 @@ permalink: /arrow/typeclasses/monoid/ -`Monoid` extends the `Semigroup` type class, adding an `empty` method to semigroup's `combine`. The empty method must return a value that, when combined with any other instance of that type, returns the other instance, i.e., +`Monoid` extends the `Semigroup` type class, adding an `empty` function to semigroup's `combine`. The empty method must return a value that, when combined with any other instance of that type, returns the other instance, i.e., ```kotlin -(combine(x, empty) == combine(empty, x) == x) +(a.combine(empty()) == empty().combine(a) == a) ``` -For example, if we have a `Monoid` with `combine` defined as string concatenation, then `empty = ""`. +For example, if we have a `Monoid` with `combine` defined as string concatenation, then `empty() = ""`. -Having an empty defined allows us to combine all the elements of some potentially empty collection of `T` for which a `Monoid` is defined and return a `T`, rather than an `Option` as we have a sensible default to fall back to. +Having `empty` defined allows us to combine all the elements of some potentially empty collection of `T` for which a `Monoid` is defined and return a `T`, rather than an `Option` as we have a sensible default to fall back to. -And let's see the instance of Monoid in action. +Let's see the instance of Monoid in action: ```kotlin:ank -import arrow.* -import arrow.core.extensions.* -import arrow.typeclasses.* +import arrow.core.string +import arrow.typeclasses.Monoid -String.monoid().run { empty() } +Monoid.string().run { empty() } ``` ```kotlin:ank -String.monoid().run { - listOf("Λ", "R", "R", "O", "W").combineAll() +Monoid.string().run { + listOf("Λ", "R", "R", "O", "W").combineAll() } ``` ```kotlin:ank -import arrow.core.* -import arrow.core.extensions.option.monoid.* +import arrow.core.Option +import arrow.core.Some +import arrow.core.int +import arrow.core.option +import arrow.typeclasses.Monoid -Option.monoid(Int.monoid()).run { listOf>(Some(1), Some(1)).combineAll() } +Monoid.option(Monoid.int()).run { listOf>(Some(1), Some(1)).combineAll() } ``` The advantage of using these type class provided methods, rather than the specific ones for each type, is that we can compose monoids to allow us to operate on more complex types, for example. @@ -47,39 +49,52 @@ The advantage of using these type class provided methods, rather than the specif This is also true if we define our own instances. As an example, let's use `Foldable`'s `foldMap`, which maps over values accumulating the results, using the available `Monoid` for the type mapped onto. ```kotlin:ank -import arrow.core.* -import arrow.core.extensions.list.foldable.foldMap +import arrow.core.foldMap +import arrow.core.identity +import arrow.core.int +import arrow.typeclasses.Monoid -listOf(1, 2, 3, 4, 5).k().foldMap(Int.monoid(), ::identity) +listOf(1, 2, 3, 4, 5).foldMap(Monoid.int(), ::identity) ``` ```kotlin:ank -listOf(1, 2, 3, 4, 5).k().foldMap(String.monoid(), { it.toString() }) +import arrow.core.foldMap +import arrow.core.string +import arrow.typeclasses.Monoid + +listOf(1, 2, 3, 4, 5).foldMap(Monoid.string()) { it.toString() } ``` -To use this with a function that produces a tuple, we can define a Monoid for a tuple that will be valid for any tuple where the types it contains also have a Monoid available. +To use this with a function that produces a pair, we can define a Monoid for a pair that will be valid for any pair where the types it contains also have a Monoid available. ```kotlin:ank:silent -fun monoidTuple(MA: Monoid, MB: Monoid): Monoid> = - object: Monoid> { - - override fun Tuple2.combine(y: Tuple2): Tuple2 { - val (xa, xb) = this - val (ya, yb) = y - return Tuple2(MA.run { xa.combine(ya) }, MB.run { xb.combine(yb) }) +import arrow.typeclasses.Monoid + +fun monoidPair(MA: Monoid, MB: Monoid): Monoid> = + object : Monoid> { + + override fun Pair.combine(other: Pair): Pair { + val (thisA, thisB) = this + val (otherA, otherB) = other + return Pair(MA.run { thisA.combine(otherA) }, MB.run { thisB.combine(otherB) }) + } + + override fun empty(): Pair = Pair(MA.empty(), MB.empty()) } - - override fun empty(): Tuple2 = Tuple2(MA.empty(), MB.empty()) - } ``` This way, we are able to combine both values in one pass, hurrah! ```kotlin:ank -val M = monoidTuple(Int.monoid(), String.monoid()) -val list = listOf(1, 1).k() +import arrow.core.foldMap +import arrow.core.int +import arrow.core.string +import arrow.typeclasses.Monoid + +val M = monoidPair(Monoid.int(), Monoid.string()) +val list = listOf(1, 1) list.foldMap(M) { n: Int -> - Tuple2(n, n.toString()) + Pair(n, n.toString()) } ``` diff --git a/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md b/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md index 9f578577e77..689928d3e92 100644 --- a/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md +++ b/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md @@ -31,34 +31,41 @@ For example, `Int` values are combined using addition by default, but multiplica Now that you've learned about the Semigroup instance for Int, try to guess how it works in the following examples: ```kotlin:ank -import arrow.* -import arrow.core.extensions.* +import arrow.core.int +import arrow.typeclasses.Semigroup -Int.semigroup().run { 1.combine(2) } +Semigroup.int().run { 1.combine(2) } ``` ```kotlin:ank -import arrow.core.* -import arrow.core.extensions.* -import arrow.core.extensions.listk.semigroup.* +import arrow.core.list +import arrow.typeclasses.Semigroup -ListK.semigroup().run { - listOf(1, 2, 3).k().combine(listOf(4, 5, 6).k()) +Semigroup.list().run { + listOf(1, 2, 3).combine(listOf(4, 5, 6)) } ``` ```kotlin:ank -import arrow.core.* -import arrow.core.extensions.option.semigroup.semigroup +import arrow.core.Option +import arrow.core.int +import arrow.core.option +import arrow.typeclasses.Semigroup -Option.semigroup(Int.semigroup()).run { - Option(1).combine(Option(2)) +Semigroup.option(Semigroup.int()).run { + Option(1).combine(Option(2)) } ``` ```kotlin:ank -Option.semigroup(Int.semigroup()).run { - Option(1).combine(None) +import arrow.core.Option +import arrow.core.None +import arrow.core.int +import arrow.core.option +import arrow.typeclasses.Semigroup + +Semigroup.option(Semigroup.int()).run { + Option(1).combine(None) } ``` @@ -67,8 +74,8 @@ Many of these types have methods defined directly on them, which allow for this Additionally, `Semigroup` adds `+` syntax to all types for which a Semigroup instance exists: ```kotlin:ank -Option.semigroup(Int.semigroup()).run { - Option(1) + Option(2) +Semigroup.option(Semigroup.int()).run { + Option(1) + Option(2) } ``` From be5d11e292a9616ad3cdc9b06bcb0fd2439f21b0 Mon Sep 17 00:00:00 2001 From: Alberto Ballano Date: Tue, 16 Mar 2021 12:20:34 +0100 Subject: [PATCH 2/3] Fix indentation --- .../docs/arrow/typeclasses/monoid/README.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md index 2dd149ed596..a37dde18b02 100644 --- a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md +++ b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md @@ -30,7 +30,7 @@ Monoid.string().run { empty() } ```kotlin:ank Monoid.string().run { - listOf("Λ", "R", "R", "O", "W").combineAll() + listOf("Λ", "R", "R", "O", "W").combineAll() } ``` @@ -71,16 +71,16 @@ To use this with a function that produces a pair, we can define a Monoid for a p import arrow.typeclasses.Monoid fun monoidPair(MA: Monoid, MB: Monoid): Monoid> = - object : Monoid> { - - override fun Pair.combine(other: Pair): Pair { - val (thisA, thisB) = this - val (otherA, otherB) = other - return Pair(MA.run { thisA.combine(otherA) }, MB.run { thisB.combine(otherB) }) - } - - override fun empty(): Pair = Pair(MA.empty(), MB.empty()) + object : Monoid> { + + override fun Pair.combine(other: Pair): Pair { + val (thisA, thisB) = this + val (otherA, otherB) = other + return Pair(MA.run { thisA.combine(otherA) }, MB.run { thisB.combine(otherB) }) } + + override fun empty(): Pair = Pair(MA.empty(), MB.empty()) +} ``` This way, we are able to combine both values in one pass, hurrah! @@ -95,6 +95,6 @@ val M = monoidPair(Monoid.int(), Monoid.string()) val list = listOf(1, 1) list.foldMap(M) { n: Int -> - Pair(n, n.toString()) + Pair(n, n.toString()) } ``` From 4ee3e37b9cd5a45808b49bfa1be0bc410ceb5205 Mon Sep 17 00:00:00 2001 From: danieh Date: Thu, 25 Mar 2021 17:17:17 +0100 Subject: [PATCH 3/3] Update typeclasses docs (#2339) * Move list extensions into companion object * Remove some imports not needed anymore * Minor change * Remove some more imports --- .../src/main/kotlin/arrow/typeclasses/Monoid.kt | 6 ++++-- .../src/main/kotlin/arrow/typeclasses/Semiring.kt | 3 --- arrow-site/docs/docs/arrow/typeclasses/monoid/README.md | 7 ------- arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md | 6 ------ 4 files changed, 4 insertions(+), 18 deletions(-) diff --git a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Monoid.kt b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Monoid.kt index 966979e97b8..698fa2ad566 100644 --- a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Monoid.kt +++ b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Monoid.kt @@ -1,5 +1,6 @@ package arrow.typeclasses +import arrow.KindDeprecation import arrow.core.Const import arrow.core.Either import arrow.core.Endo @@ -12,10 +13,11 @@ import arrow.core.compose import arrow.core.flatten import arrow.core.identity import kotlin.collections.plus as _plus -import arrow.KindDeprecation @Deprecated(KindDeprecation) -class ForMonoid private constructor() { companion object } +class ForMonoid private constructor() { + companion object +} @Deprecated(KindDeprecation) typealias MonoidOf = arrow.Kind diff --git a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt index 2d713dc31ea..cbb7d66d4ad 100644 --- a/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt +++ b/arrow-libs/core/arrow-core-data/src/main/kotlin/arrow/typeclasses/Semiring.kt @@ -27,7 +27,6 @@ import arrow.documented * Here a some examples: * * ```kotlin:ank:playground - * import arrow.core.int * import arrow.typeclasses.Semiring * * fun main(args: Array) { @@ -40,7 +39,6 @@ import arrow.documented * ``` * * ```kotlin:ank:playground - * import arrow.core.int * import arrow.typeclasses.Semiring * * fun main(args: Array) { @@ -55,7 +53,6 @@ import arrow.documented * The type class `Semiring` also has support for the `+` `*` syntax: * * ```kotlin:ank:playground - * import arrow.core.int * import arrow.typeclasses.Semiring * * fun main(args: Array) { diff --git a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md index a37dde18b02..d240cc6ad99 100644 --- a/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md +++ b/arrow-site/docs/docs/arrow/typeclasses/monoid/README.md @@ -22,7 +22,6 @@ Having `empty` defined allows us to combine all the elements of some potentially Let's see the instance of Monoid in action: ```kotlin:ank -import arrow.core.string import arrow.typeclasses.Monoid Monoid.string().run { empty() } @@ -37,8 +36,6 @@ Monoid.string().run { ```kotlin:ank import arrow.core.Option import arrow.core.Some -import arrow.core.int -import arrow.core.option import arrow.typeclasses.Monoid Monoid.option(Monoid.int()).run { listOf>(Some(1), Some(1)).combineAll() } @@ -51,7 +48,6 @@ This is also true if we define our own instances. As an example, let's use `Fold ```kotlin:ank import arrow.core.foldMap import arrow.core.identity -import arrow.core.int import arrow.typeclasses.Monoid listOf(1, 2, 3, 4, 5).foldMap(Monoid.int(), ::identity) @@ -59,7 +55,6 @@ listOf(1, 2, 3, 4, 5).foldMap(Monoid.int(), ::identity) ```kotlin:ank import arrow.core.foldMap -import arrow.core.string import arrow.typeclasses.Monoid listOf(1, 2, 3, 4, 5).foldMap(Monoid.string()) { it.toString() } @@ -87,8 +82,6 @@ This way, we are able to combine both values in one pass, hurrah! ```kotlin:ank import arrow.core.foldMap -import arrow.core.int -import arrow.core.string import arrow.typeclasses.Monoid val M = monoidPair(Monoid.int(), Monoid.string()) diff --git a/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md b/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md index 689928d3e92..8936fd021d5 100644 --- a/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md +++ b/arrow-site/docs/docs/arrow/typeclasses/semigroup/README.md @@ -31,14 +31,12 @@ For example, `Int` values are combined using addition by default, but multiplica Now that you've learned about the Semigroup instance for Int, try to guess how it works in the following examples: ```kotlin:ank -import arrow.core.int import arrow.typeclasses.Semigroup Semigroup.int().run { 1.combine(2) } ``` ```kotlin:ank -import arrow.core.list import arrow.typeclasses.Semigroup Semigroup.list().run { @@ -48,8 +46,6 @@ Semigroup.list().run { ```kotlin:ank import arrow.core.Option -import arrow.core.int -import arrow.core.option import arrow.typeclasses.Semigroup Semigroup.option(Semigroup.int()).run { @@ -60,8 +56,6 @@ Semigroup.option(Semigroup.int()).run { ```kotlin:ank import arrow.core.Option import arrow.core.None -import arrow.core.int -import arrow.core.option import arrow.typeclasses.Semigroup Semigroup.option(Semigroup.int()).run {