From 0e0f0d4a661f4ba4978789bb055330ae8a8ad0c5 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 11:13:33 +0000 Subject: [PATCH 01/33] Add methods from seimgroup/monoid to semigroupk/monoidk --- core/src/main/scala/cats/MonoidK.scala | 65 +++++++++++++++++++++++ core/src/main/scala/cats/SemigroupK.scala | 65 +++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 662cdb2a94..da8ac73e67 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -36,6 +36,23 @@ import simulacrum.typeclass */ def empty[A]: F[A] + /** + * Tests if `a` is the identity. + * + * Example: + * {{{ + * scala> import cats.kernel.instances.string._ + * + * scala> Monoid[String].isEmpty("") + * res0: Boolean = true + * + * scala> Monoid[String].isEmpty("something") + * res1: Boolean = false + * }}} + */ + def isEmpty[A](a: F[A])(implicit ev: Eq[F[A]]): Boolean = + ev.eqv(a, empty) + /** * Given a type A, create a concrete Monoid[F[A]]. * @@ -67,6 +84,54 @@ import simulacrum.typeclass new ComposedMonoidK[F, G] { val F: MonoidK[F] = self } + + /** + * Return `a` appended to itself `n` times. + * + * Example: + * {{{ + * scala> import cats.kernel.instances.string._ + * + * scala> Monoid[String].combineN("ha", 3) + * res0: String = hahaha + * + * scala> Monoid[String].combineN("ha", 0) + * res1: String = "" + * }}} + */ + override def combineNK[A](a: F[A], n: Int): F[A] = + if (n < 0) throw new IllegalArgumentException("Repeated combining for monoids must have n >= 0") + else if (n == 0) empty[A] + else repeatedCombineNK(a, n) + + /** + * Given a sequence of `as`, sum them using the monoid and return the total. + * + * Example: + * {{{ + * scala> import cats.kernel.instances.string._ + * + * scala> Monoid[String].combineAll(List("One ", "Two ", "Three")) + * res0: String = One Two Three + * + * scala> Monoid[String].combineAll(List.empty) + * res1: String = "" + * }}} + */ + def combineAllK[A](as: IterableOnce[F[A]]): F[A] = + as.iterator.foldLeft(empty[A])(combineK[A]) + + override def combineAllOptionK[A](as: IterableOnce[F[A]]): Option[F[A]] = + if (as.iterator.isEmpty) None else Some(combineAllK(as)) + + override def reverse: MonoidK[F] = + new MonoidK[F] { + def empty[A] = self.empty + def combineK[A](a: F[A], b: F[A]) = self.combineK(b, a) + // a + a + a + ... is the same when reversed + override def combineNK[A](a: F[A], n: Int): F[A] = self.combineNK(a, n) + override def reverse = self + } } object MonoidK { diff --git a/core/src/main/scala/cats/SemigroupK.scala b/core/src/main/scala/cats/SemigroupK.scala index 1c25bd4cfa..449fe5f16e 100644 --- a/core/src/main/scala/cats/SemigroupK.scala +++ b/core/src/main/scala/cats/SemigroupK.scala @@ -1,5 +1,6 @@ package cats +import scala.annotation.tailrec import scala.collection.immutable.{Seq, SortedMap, SortedSet} import simulacrum.typeclass import cats.data.Ior @@ -108,6 +109,70 @@ import cats.data.Ior */ def sum[A, B](fa: F[A], fb: F[B])(implicit F: Functor[F]): F[Either[A, B]] = combineK(F.map(fa)(Left(_)), F.map(fb)(Right(_))) + + /** + * Return `a` combined with itself `n` times. + * + * Example: + * {{{ + * scala> import cats.kernel.instances.int._ + * scala> import cats.kernel.instances.string._ + * + * scala> Semigroup[Int].combineN(1, 10) + * res0: Int = 10 + * + * scala> Semigroup[String].combineN("ha", 3) + * res1: String = hahaha + * }}} + */ + def combineNK[A](a: F[A], n: Int): F[A] = + if (n <= 0) throw new IllegalArgumentException("Repeated combining for semigroupKs must have n > 0") + else repeatedCombineNK(a, n) + + /** + * Return `a` combined with itself more than once. + */ + protected[this] def repeatedCombineNK[A](a: F[A], n: Int): F[A] = { + @tailrec def loop(b: F[A], k: Int, extra: F[A]): F[A] = + if (k == 1) combineK(b, extra) + else { + val x = if ((k & 1) == 1) combineK(b, extra) else extra + loop(combineK(b, b), k >>> 1, x) + } + if (n == 1) a else loop(a, n - 1, a) + } + + /** + * Given a sequence of `as`, combine them and return the total. + * + * If the sequence is empty, returns None. Otherwise, returns Some(total). + * + * Example: + * {{{ + * scala> import cats.kernel.instances.string._ + * + * scala> Semigroup[String].combineAllOption(List("One ", "Two ", "Three")) + * res0: Option[String] = Some(One Two Three) + * + * scala> Semigroup[String].combineAllOption(List.empty) + * res1: Option[String] = None + * }}} + */ + def combineAllOptionK[A](as: IterableOnce[F[A]]): Option[F[A]] = + as.iterator.reduceOption(combineK[A]) + + /** + * return a semigroupK that reverses the order + * so combine(a, b) == reverse.combine(b, a) + */ + def reverse: SemigroupK[F] = + new SemigroupK[F] { + def combineK[A](a: F[A], b: F[A]): F[A] = self.combineK(b, a) + // a + a + a + ... is the same when reversed + override def combineNK[A](a: F[A], n: Int): F[A] = self.combineNK(a, n) + override def reverse = self + } + } object SemigroupK extends ScalaVersionSpecificMonoidKInstances with SemigroupKInstances0 { From 9ea68b46bbe1cd6634141e968b57a57128b0df2c Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 11:49:48 +0000 Subject: [PATCH 02/33] Update scaladoc --- core/src/main/scala/cats/MonoidK.scala | 25 ++++++++++++----------- core/src/main/scala/cats/SemigroupK.scala | 21 ++++++++----------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index da8ac73e67..0e4589be3c 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -86,17 +86,18 @@ import simulacrum.typeclass } /** - * Return `a` appended to itself `n` times. + * Return `a` combined with itself `n` times. * * Example: * {{{ - * scala> import cats.kernel.instances.string._ + * scala> import cats.kernel.instances.list._ * - * scala> Monoid[String].combineN("ha", 3) - * res0: String = hahaha + * scala> SemigroupK[List].combineNK(List(1), 5) + * res0: List[Int] = List(1,1,1,1,1) + + * scala> MonoidK[List].combineN(List("ha"), 0) + * res1: List[String] = List() * - * scala> Monoid[String].combineN("ha", 0) - * res1: String = "" * }}} */ override def combineNK[A](a: F[A], n: Int): F[A] = @@ -105,17 +106,17 @@ import simulacrum.typeclass else repeatedCombineNK(a, n) /** - * Given a sequence of `as`, sum them using the monoid and return the total. + * Given a sequence of `as`, sum them using the monoidK and return the total. * * Example: * {{{ - * scala> import cats.kernel.instances.string._ + * scala> import cats.kernel.instances.list._ * - * scala> Monoid[String].combineAll(List("One ", "Two ", "Three")) - * res0: String = One Two Three + * scala> MonoidK[List].combineAllK(List(List("One"), List("Two"), List("Three"))) + * res0: List[String] = List("One", List("Two"), List("Three")) * - * scala> Monoid[String].combineAll(List.empty) - * res1: String = "" + * scala> MonoidK[List].combineAll[String](List.empty) + * res1: List[String] = List() * }}} */ def combineAllK[A](as: IterableOnce[F[A]]): F[A] = diff --git a/core/src/main/scala/cats/SemigroupK.scala b/core/src/main/scala/cats/SemigroupK.scala index 449fe5f16e..0247b5d1d9 100644 --- a/core/src/main/scala/cats/SemigroupK.scala +++ b/core/src/main/scala/cats/SemigroupK.scala @@ -115,14 +115,11 @@ import cats.data.Ior * * Example: * {{{ - * scala> import cats.kernel.instances.int._ - * scala> import cats.kernel.instances.string._ + * scala> import cats.kernel.instances.list._ * - * scala> Semigroup[Int].combineN(1, 10) - * res0: Int = 10 + * scala> SemigroupK[List].combineNK(List(1), 5) + * res0: List[Int] = List(1,1,1,1,1) * - * scala> Semigroup[String].combineN("ha", 3) - * res1: String = hahaha * }}} */ def combineNK[A](a: F[A], n: Int): F[A] = @@ -149,13 +146,13 @@ import cats.data.Ior * * Example: * {{{ - * scala> import cats.kernel.instances.string._ + * scala> import cats.kernel.instances.list._ * - * scala> Semigroup[String].combineAllOption(List("One ", "Two ", "Three")) - * res0: Option[String] = Some(One Two Three) + * scala> SemigroupK[List].combineAllOptionK(List(List("One"), List("Two"), List("Three"))) + * res0: Option[List[String]] = Some(List("One"), List("Two"), List("Three")) * - * scala> Semigroup[String].combineAllOption(List.empty) - * res1: Option[String] = None + * scala> SemigroupK[List].combineAllOptionK[String](List.empty) + * res1: Option[List[String]] = None * }}} */ def combineAllOptionK[A](as: IterableOnce[F[A]]): Option[F[A]] = @@ -163,7 +160,7 @@ import cats.data.Ior /** * return a semigroupK that reverses the order - * so combine(a, b) == reverse.combine(b, a) + * so combineK(a, b) == reverse.combineK(b, a) */ def reverse: SemigroupK[F] = new SemigroupK[F] { From 9f348f7abdf95bec5eea5a7092cb451977a3119c Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 12:13:31 +0000 Subject: [PATCH 03/33] More scaladoc --- core/src/main/scala/cats/MonoidK.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 0e4589be3c..a849fde3bf 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -41,12 +41,12 @@ import simulacrum.typeclass * * Example: * {{{ - * scala> import cats.kernel.instances.string._ + * scala> import cats.kernel.instances.list._ * - * scala> Monoid[String].isEmpty("") + * scala> MonoidK[List].isEmpty(List.empty[String]) * res0: Boolean = true * - * scala> Monoid[String].isEmpty("something") + * scala> MonoidK[List].isEmpty(List("something")) * res1: Boolean = false * }}} */ From b0b1404c7ef5d67ed1b286f4fbe1cd2bca482d4f Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 12:14:05 +0000 Subject: [PATCH 04/33] Remove duplicate tests --- tests/src/test/scala/cats/tests/SemigroupSuite.scala | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/src/test/scala/cats/tests/SemigroupSuite.scala b/tests/src/test/scala/cats/tests/SemigroupSuite.scala index bf8b6490e8..a4753757f0 100644 --- a/tests/src/test/scala/cats/tests/SemigroupSuite.scala +++ b/tests/src/test/scala/cats/tests/SemigroupSuite.scala @@ -7,11 +7,6 @@ import cats.kernel.laws.discipline.SemigroupTests import org.scalacheck.Prop._ class SemigroupSuite extends CatsSuite { - { - Invariant[Semigroup] - Semigroupal[Semigroup] - InvariantMonoidal[Semigroup] - } { Invariant[Semigroup] From 72fe7145ee99dbfb336e43a2e0130b185dc5b776 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 14:48:13 +0000 Subject: [PATCH 05/33] Fix 2.12 build --- core/src/main/scala/cats/MonoidK.scala | 3 +++ core/src/main/scala/cats/SemigroupK.scala | 2 ++ 2 files changed, 5 insertions(+) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index a849fde3bf..2aa4e1ecc6 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -1,5 +1,7 @@ package cats +import cats.kernel.compat.scalaVersionSpecific._ + import simulacrum.typeclass /** @@ -135,6 +137,7 @@ import simulacrum.typeclass } } +@suppressUnusedImportWarningForScalaVersionSpecific object MonoidK { /* ======================================================================== */ diff --git a/core/src/main/scala/cats/SemigroupK.scala b/core/src/main/scala/cats/SemigroupK.scala index 0247b5d1d9..6e35622b2c 100644 --- a/core/src/main/scala/cats/SemigroupK.scala +++ b/core/src/main/scala/cats/SemigroupK.scala @@ -4,6 +4,7 @@ import scala.annotation.tailrec import scala.collection.immutable.{Seq, SortedMap, SortedSet} import simulacrum.typeclass import cats.data.Ior +import cats.kernel.compat.scalaVersionSpecific._ /** * SemigroupK is a universal semigroup which operates on kinds. @@ -172,6 +173,7 @@ import cats.data.Ior } +@suppressUnusedImportWarningForScalaVersionSpecific object SemigroupK extends ScalaVersionSpecificMonoidKInstances with SemigroupKInstances0 { def align[F[_]: SemigroupK: Functor]: Align[F] = new Align[F] { From 9fb1bd1292de2a21e2f122818ac50d9a1515a36d Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 17:02:02 +0000 Subject: [PATCH 06/33] Fix docs --- core/src/main/scala/cats/MonoidK.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 2aa4e1ecc6..0819d64632 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -97,7 +97,7 @@ import simulacrum.typeclass * scala> SemigroupK[List].combineNK(List(1), 5) * res0: List[Int] = List(1,1,1,1,1) - * scala> MonoidK[List].combineN(List("ha"), 0) + * scala> MonoidK[List].combineNK(List("ha"), 0) * res1: List[String] = List() * * }}} @@ -117,7 +117,7 @@ import simulacrum.typeclass * scala> MonoidK[List].combineAllK(List(List("One"), List("Two"), List("Three"))) * res0: List[String] = List("One", List("Two"), List("Three")) * - * scala> MonoidK[List].combineAll[String](List.empty) + * scala> MonoidK[List].combineAllK[String](List.empty) * res1: List[String] = List() * }}} */ From e6fa448d519d17431aeb593a050a7245d0423f8a Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 15 Dec 2021 17:41:18 +0000 Subject: [PATCH 07/33] Actually fix docs :facepalm: --- core/src/main/scala/cats/MonoidK.scala | 10 ++-------- core/src/main/scala/cats/SemigroupK.scala | 8 ++------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 0819d64632..095bf210f4 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -43,8 +43,6 @@ import simulacrum.typeclass * * Example: * {{{ - * scala> import cats.kernel.instances.list._ - * * scala> MonoidK[List].isEmpty(List.empty[String]) * res0: Boolean = true * @@ -92,10 +90,8 @@ import simulacrum.typeclass * * Example: * {{{ - * scala> import cats.kernel.instances.list._ - * * scala> SemigroupK[List].combineNK(List(1), 5) - * res0: List[Int] = List(1,1,1,1,1) + * res0: List[Int] = List(1, 1, 1, 1, 1) * scala> MonoidK[List].combineNK(List("ha"), 0) * res1: List[String] = List() @@ -112,10 +108,8 @@ import simulacrum.typeclass * * Example: * {{{ - * scala> import cats.kernel.instances.list._ - * * scala> MonoidK[List].combineAllK(List(List("One"), List("Two"), List("Three"))) - * res0: List[String] = List("One", List("Two"), List("Three")) + * res0: List[String] = List(One, Two, Three) * * scala> MonoidK[List].combineAllK[String](List.empty) * res1: List[String] = List() diff --git a/core/src/main/scala/cats/SemigroupK.scala b/core/src/main/scala/cats/SemigroupK.scala index 6e35622b2c..f376428213 100644 --- a/core/src/main/scala/cats/SemigroupK.scala +++ b/core/src/main/scala/cats/SemigroupK.scala @@ -116,10 +116,8 @@ import cats.kernel.compat.scalaVersionSpecific._ * * Example: * {{{ - * scala> import cats.kernel.instances.list._ - * * scala> SemigroupK[List].combineNK(List(1), 5) - * res0: List[Int] = List(1,1,1,1,1) + * res0: List[Int] = List(1, 1, 1, 1, 1) * * }}} */ @@ -147,10 +145,8 @@ import cats.kernel.compat.scalaVersionSpecific._ * * Example: * {{{ - * scala> import cats.kernel.instances.list._ - * * scala> SemigroupK[List].combineAllOptionK(List(List("One"), List("Two"), List("Three"))) - * res0: Option[List[String]] = Some(List("One"), List("Two"), List("Three")) + * res0: Option[List[String]] = Some(List(One, Two, Three)) * * scala> SemigroupK[List].combineAllOptionK[String](List.empty) * res1: Option[List[String]] = None From c4a620e134abab6469116773c69321dc86c300d8 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 16:18:46 +0000 Subject: [PATCH 08/33] Port semigroup/monoid laws to semigroupk/monoidk --- .../main/scala/cats/laws/MonoidKLaws.scala | 12 +++++ .../main/scala/cats/laws/SemigroupKLaws.scala | 45 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/laws/src/main/scala/cats/laws/MonoidKLaws.scala b/laws/src/main/scala/cats/laws/MonoidKLaws.scala index e8ad9251ec..71b45fe047 100644 --- a/laws/src/main/scala/cats/laws/MonoidKLaws.scala +++ b/laws/src/main/scala/cats/laws/MonoidKLaws.scala @@ -12,6 +12,18 @@ trait MonoidKLaws[F[_]] extends SemigroupKLaws[F] { def monoidKRightIdentity[A](a: F[A]): IsEq[F[A]] = F.combineK(a, F.empty) <-> a + + def repeatK0[A](x: F[A]): IsEq[F[A]] = + F.combineNK(x, 0) <-> F.empty + + def collectK0[A](x: A): IsEq[F[A]] = + F.combineAllK(Nil) <-> F.empty + + def combineAllK[A](xs: Vector[F[A]]): IsEq[F[A]] = + F.combineAllK(xs) <-> (F.empty[A] +: xs).reduce(F.combineK[A]) + + def isId[A](x: F[A], eqv: Eq[F[A]]): IsEq[Boolean] = + eqv.eqv(x, F.empty) <-> F.isEmpty(x)(eqv) } object MonoidKLaws { diff --git a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala index 34cd1792ce..b616ce8926 100644 --- a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala +++ b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala @@ -9,6 +9,51 @@ trait SemigroupKLaws[F[_]] { def semigroupKAssociative[A](a: F[A], b: F[A], c: F[A]): IsEq[F[A]] = F.combineK(F.combineK(a, b), c) <-> F.combineK(a, F.combineK(b, c)) + + def repeat1K[A](a: F[A]): IsEq[F[A]] = + F.combineNK(a, 1) <-> a + + def repeat2[A](a: F[A]): IsEq[F[A]] = + F.combineNK(a, 2) <-> F.combineK(a, a) + + def combineAllOptionK[A](xs: Vector[F[A]]): IsEq[Option[F[A]]] = + F.combineAllOptionK(xs) <-> xs.reduceOption(F.combineK[A]) + + def reverseReversesK[A](a: F[A], b: F[A]): IsEq[F[A]] = + F.combineK(a, b) <-> F.reverse.combineK(b, a) + + def reverseRepeat1K[A](a: F[A]): IsEq[F[A]] = { + val rev = F.reverse + rev.combineNK(a, 1) <-> a + } + + def reverseRepeat2K[A](a: F[A]): IsEq[F[A]] = { + val rev = F.reverse + rev.combineNK(a, 2) <-> rev.combineK(a, a) + } + + def reverseCombineAllOptionK[A](xs: Vector[F[A]]): IsEq[Option[F[A]]] = { + val rev = F.reverse + rev.combineAllOptionK(xs) <-> xs.reduceOption(rev.combineK[A]) + } + + // def intercalateIntercalates[A](a: F[A], m: F[A], b: F[A]): IsEq[F[A]] = + // F.combineK(a, F.combineK(m, b)) <-> F.intercalate(m).combineK(a, b) + + // def intercalateRepeat1(m: A, a: A): IsEq[A] = { + // val withMiddle = S.intercalate(m) + // withMiddle.combineN(a, 1) <-> a + // } + + // def intercalateRepeat2(m: A, a: A): IsEq[A] = { + // val withMiddle = S.intercalate(m) + // withMiddle.combineN(a, 2) <-> withMiddle.combine(a, a) + // } + + // def intercalateCombineAllOption(m: A, xs: Vector[A]): IsEq[Option[A]] = { + // val withMiddle = S.intercalate(m) + // withMiddle.combineAllOption(xs) <-> xs.reduceOption(withMiddle.combine) + // } } object SemigroupKLaws { From 0d7de79fa1309a30411cbf9dbe9619ee894a3986 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 16:34:11 +0000 Subject: [PATCH 09/33] Cleanup comments --- .../main/scala/cats/laws/SemigroupKLaws.scala | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala index b616ce8926..3d3755ba4a 100644 --- a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala +++ b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala @@ -37,23 +37,6 @@ trait SemigroupKLaws[F[_]] { rev.combineAllOptionK(xs) <-> xs.reduceOption(rev.combineK[A]) } - // def intercalateIntercalates[A](a: F[A], m: F[A], b: F[A]): IsEq[F[A]] = - // F.combineK(a, F.combineK(m, b)) <-> F.intercalate(m).combineK(a, b) - - // def intercalateRepeat1(m: A, a: A): IsEq[A] = { - // val withMiddle = S.intercalate(m) - // withMiddle.combineN(a, 1) <-> a - // } - - // def intercalateRepeat2(m: A, a: A): IsEq[A] = { - // val withMiddle = S.intercalate(m) - // withMiddle.combineN(a, 2) <-> withMiddle.combine(a, a) - // } - - // def intercalateCombineAllOption(m: A, xs: Vector[A]): IsEq[Option[A]] = { - // val withMiddle = S.intercalate(m) - // withMiddle.combineAllOption(xs) <-> xs.reduceOption(withMiddle.combine) - // } } object SemigroupKLaws { From 9264e7c99d9dcca87c19d26b847cc2b0d8be4b1c Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 16:56:22 +0000 Subject: [PATCH 10/33] Optimize combineAllK for Option --- core/src/main/scala/cats/instances/option.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index 04fd8a4ded..c9d6421f25 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -25,6 +25,12 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { def combineK[A](x: Option[A], y: Option[A]): Option[A] = if (x.isDefined) x else y + override def combineAllOptionK[A](as: IterableOnce[Option[A]]): Option[Option[A]] = + as.iterator.find(_.isDefined) + + override def combineAllK[A](as: IterableOnce[Option[A]]): Option[A] = + as.iterator.find(_.isDefined).getOrElse(None) + override def prependK[A](a: A, fa: Option[A]): Option[A] = Some(a) override def appendK[A](fa: Option[A], a: A): Option[A] = if (fa.isDefined) fa else Some(a) From 5242b6c7d63bd27dc2552458202a969b7cd610e8 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 16:57:08 +0000 Subject: [PATCH 11/33] DRY --- core/src/main/scala/cats/instances/option.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index c9d6421f25..9c301a8133 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -29,7 +29,7 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { as.iterator.find(_.isDefined) override def combineAllK[A](as: IterableOnce[Option[A]]): Option[A] = - as.iterator.find(_.isDefined).getOrElse(None) + combineAllOptionK(as).getOrElse(None) override def prependK[A](a: A, fa: Option[A]): Option[A] = Some(a) override def appendK[A](fa: Option[A], a: A): Option[A] = if (fa.isDefined) fa else Some(a) From 93de0b8fec0b234e156bbbc8277ab57a3c5af83c Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 17:30:39 +0000 Subject: [PATCH 12/33] Override combineAllK for list --- core/src/main/scala/cats/instances/list.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 0ca62ad851..ba00d81f5e 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -19,6 +19,15 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def combineK[A](x: List[A], y: List[A]): List[A] = x ::: y + override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = + { + val iter = as.iterator + if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toList) + } + + override def combineAllK[A](as: IterableOnce[List[A]]): List[A] = + as.iterator.flatMap(_.iterator).toList + override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa override def appendK[A](fa: List[A], a: A): List[A] = fa :+ a From c2d3d4f8be4670a68aa69723405715f1974e19af Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 17:36:17 +0000 Subject: [PATCH 13/33] Override combineAllK for Seq --- core/src/main/scala/cats/instances/seq.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index 88bcd9e010..d7c8ba42f7 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -17,6 +17,14 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { def combineK[A](x: Seq[A], y: Seq[A]): Seq[A] = x ++ y + override def combineAllOptionK[A](as: IterableOnce[Seq[A]]): Option[Seq[A]] = { + val iter = as.iterator + if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toSeq) + } + + override def combineAllK[A](as: IterableOnce[Seq[A]]): Seq[A] = + as.iterator.flatMap(_.iterator).toSeq + override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa override def appendK[A](fa: Seq[A], a: A): Seq[A] = fa :+ a From 7099f59c9596b50d0b87bb2a6cfa550855ca43e5 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 17:39:27 +0000 Subject: [PATCH 14/33] Override combineAllK for vector --- core/src/main/scala/cats/instances/vector.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 692f536015..f3d69ecccd 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -18,6 +18,14 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { def combineK[A](x: Vector[A], y: Vector[A]): Vector[A] = x ++ y + override def combineAllOptionK[A](as: IterableOnce[Vector[A]]): Option[Vector[A]] = { + val iter = as.iterator + if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toVector) + } + + override def combineAllK[A](as: IterableOnce[Vector[A]]): Vector[A] = + as.iterator.flatMap(_.iterator).toVector + override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa override def appendK[A](fa: Vector[A], a: A): Vector[A] = fa :+ a From 9c07ddeb33209b41f6ad94bdfab950c062130427 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 17:45:27 +0000 Subject: [PATCH 15/33] Override combineAllK for Queue --- core/src/main/scala/cats/instances/queue.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 07de24626a..290cc66929 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -17,6 +17,14 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { def combineK[A](x: Queue[A], y: Queue[A]): Queue[A] = x ++ y + override def combineAllOptionK[A](as: IterableOnce[Queue[A]]): Option[Queue[A]] = { + val iter = as.iterator + if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).to(Queue)) + } + + override def combineAllK[A](as: IterableOnce[Queue[A]]): Queue[A] = + as.iterator.flatMap(_.iterator).to(Queue) + override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa override def appendK[A](fa: Queue[A], a: A): Queue[A] = fa.enqueue(a) From 47232bf8bc468bd8b0b6dc19d95234774fe99fab Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 12 Jan 2022 17:57:41 +0000 Subject: [PATCH 16/33] (Hopefully) fix version-specific errors for IterableOnce --- core/src/main/scala/cats/instances/list.scala | 11 ++++++----- core/src/main/scala/cats/instances/option.scala | 2 ++ core/src/main/scala/cats/instances/queue.scala | 2 ++ core/src/main/scala/cats/instances/seq.scala | 2 ++ core/src/main/scala/cats/instances/vector.scala | 2 ++ 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index ba00d81f5e..f9bb902aa9 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.{Chain, ZipList} +import cats.kernel.compat.scalaVersionSpecific._ import cats.kernel.instances.StaticMethods.wrapMutableIndexedSeq import cats.syntax.show._ @@ -19,11 +20,10 @@ trait ListInstances extends cats.kernel.instances.ListInstances { def combineK[A](x: List[A], y: List[A]): List[A] = x ::: y - override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = - { - val iter = as.iterator - if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toList) - } + override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = { + val iter = as.iterator + if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toList) + } override def combineAllK[A](as: IterableOnce[List[A]]): List[A] = as.iterator.flatMap(_.iterator).toList @@ -271,6 +271,7 @@ trait ListInstances extends cats.kernel.instances.ListInstances { } } +@suppressUnusedImportWarningForScalaVersionSpecific private[instances] trait ListInstancesBinCompat0 { implicit val catsStdTraverseFilterForList: TraverseFilter[List] = new TraverseFilter[List] { val traverse: Traverse[List] = cats.instances.list.catsStdInstancesForList diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index 9c301a8133..dbdf147d83 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -3,6 +3,7 @@ package instances import scala.annotation.tailrec import cats.data.Ior +import cats.kernel.compat.scalaVersionSpecific._ trait OptionInstances extends cats.kernel.instances.OptionInstances { @@ -218,6 +219,7 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { } } +@suppressUnusedImportWarningForScalaVersionSpecific private[instances] trait OptionInstancesBinCompat0 { implicit val catsStdTraverseFilterForOption: TraverseFilter[Option] = new TraverseFilter[Option] { val traverse: Traverse[Option] = cats.instances.option.catsStdInstancesForOption diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 290cc66929..6f796b457d 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.Chain +import cats.kernel.compat.scalaVersionSpecific._ import cats.kernel.instances.StaticMethods.wrapMutableIndexedSeq import cats.syntax.show._ import scala.annotation.tailrec @@ -178,6 +179,7 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { implicit def catsStdTraverseFilterForQueue: TraverseFilter[Queue] = QueueInstances.catsStdTraverseFilterForQueue } +@suppressUnusedImportWarningForScalaVersionSpecific private object QueueInstances { private val catsStdTraverseFilterForQueue: TraverseFilter[Queue] = new TraverseFilter[Queue] { val traverse: Traverse[Queue] = cats.instances.queue.catsStdInstancesForQueue diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index d7c8ba42f7..7b1ce6b8cf 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -2,12 +2,14 @@ package cats package instances import cats.data.{Chain, ZipSeq} +import cats.kernel.compat.scalaVersionSpecific._ import scala.annotation.tailrec import scala.collection.{+:, mutable} import scala.collection.immutable.Seq import cats.data.Ior +@suppressUnusedImportWarningForScalaVersionSpecific trait SeqInstances extends cats.kernel.instances.SeqInstances { implicit val catsStdInstancesForSeq : Traverse[Seq] with Monad[Seq] with Alternative[Seq] with CoflatMap[Seq] with Align[Seq] = diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index f3d69ecccd..922a9fa555 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.{Chain, ZipVector} +import cats.kernel.compat.scalaVersionSpecific._ import cats.syntax.show._ import scala.annotation.tailrec @@ -214,6 +215,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { } } +@suppressUnusedImportWarningForScalaVersionSpecific private[instances] trait VectorInstancesBinCompat0 { implicit val catsStdTraverseFilterForVector: TraverseFilter[Vector] = new TraverseFilter[Vector] { val traverse: Traverse[Vector] = cats.instances.vector.catsStdInstancesForVector From 4322e8b6b3dcfe817a6ec09e76e3474c4ef14899 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 11:21:38 +0000 Subject: [PATCH 17/33] Fix error message --- core/src/main/scala/cats/MonoidK.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 095bf210f4..5b4ce07413 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -99,7 +99,7 @@ import simulacrum.typeclass * }}} */ override def combineNK[A](a: F[A], n: Int): F[A] = - if (n < 0) throw new IllegalArgumentException("Repeated combining for monoids must have n >= 0") + if (n < 0) throw new IllegalArgumentException("Repeated combining for monoidKs must have n >= 0") else if (n == 0) empty[A] else repeatedCombineNK(a, n) From 05763b2b48e1457eaf708b1965f8b9adea86916b Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 11:55:12 +0000 Subject: [PATCH 18/33] Only require optimizing combineAllOptionK and not combineAllOption as well --- core/src/main/scala/cats/MonoidK.scala | 8 ++++---- core/src/main/scala/cats/instances/list.scala | 3 --- core/src/main/scala/cats/instances/option.scala | 3 --- core/src/main/scala/cats/instances/queue.scala | 3 --- core/src/main/scala/cats/instances/seq.scala | 3 --- core/src/main/scala/cats/instances/vector.scala | 3 --- 6 files changed, 4 insertions(+), 19 deletions(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 5b4ce07413..9503f69e9c 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -116,10 +116,10 @@ import simulacrum.typeclass * }}} */ def combineAllK[A](as: IterableOnce[F[A]]): F[A] = - as.iterator.foldLeft(empty[A])(combineK[A]) - - override def combineAllOptionK[A](as: IterableOnce[F[A]]): Option[F[A]] = - if (as.iterator.isEmpty) None else Some(combineAllK(as)) + combineAllOptionK(as) match { + case Some(fa) => fa + case None => empty[A] + } override def reverse: MonoidK[F] = new MonoidK[F] { diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index f9bb902aa9..203b34b02f 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -25,9 +25,6 @@ trait ListInstances extends cats.kernel.instances.ListInstances { if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toList) } - override def combineAllK[A](as: IterableOnce[List[A]]): List[A] = - as.iterator.flatMap(_.iterator).toList - override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa override def appendK[A](fa: List[A], a: A): List[A] = fa :+ a diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index dbdf147d83..3c5ee25a99 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -29,9 +29,6 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { override def combineAllOptionK[A](as: IterableOnce[Option[A]]): Option[Option[A]] = as.iterator.find(_.isDefined) - override def combineAllK[A](as: IterableOnce[Option[A]]): Option[A] = - combineAllOptionK(as).getOrElse(None) - override def prependK[A](a: A, fa: Option[A]): Option[A] = Some(a) override def appendK[A](fa: Option[A], a: A): Option[A] = if (fa.isDefined) fa else Some(a) diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 6f796b457d..a5af704bff 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -23,9 +23,6 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).to(Queue)) } - override def combineAllK[A](as: IterableOnce[Queue[A]]): Queue[A] = - as.iterator.flatMap(_.iterator).to(Queue) - override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa override def appendK[A](fa: Queue[A], a: A): Queue[A] = fa.enqueue(a) diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index 7b1ce6b8cf..e9d0b4274a 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -24,9 +24,6 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toSeq) } - override def combineAllK[A](as: IterableOnce[Seq[A]]): Seq[A] = - as.iterator.flatMap(_.iterator).toSeq - override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa override def appendK[A](fa: Seq[A], a: A): Seq[A] = fa :+ a diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 922a9fa555..ad5bbea436 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -24,9 +24,6 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toVector) } - override def combineAllK[A](as: IterableOnce[Vector[A]]): Vector[A] = - as.iterator.flatMap(_.iterator).toVector - override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa override def appendK[A](fa: Vector[A], a: A): Vector[A] = fa :+ a From c237e17e5e0309d0f92aa8233ee73a0cfb56e505 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 11:59:35 +0000 Subject: [PATCH 19/33] Avoid creating closure in comineAllOptionK for Option --- core/src/main/scala/cats/instances/option.scala | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index 3c5ee25a99..1c92c211f5 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -26,8 +26,19 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { def combineK[A](x: Option[A], y: Option[A]): Option[A] = if (x.isDefined) x else y - override def combineAllOptionK[A](as: IterableOnce[Option[A]]): Option[Option[A]] = - as.iterator.find(_.isDefined) + override def combineAllOptionK[A](as: IterableOnce[Option[A]]): Option[Option[A]] = { + val it = as.iterator + if (it.hasNext) { + while (true) { + val o = it.next() + if (o.isDefined) return Some(o) + if (!it.hasNext) return Some(None) + } + sys.error("unreachable") + } else { + return None + } + } override def prependK[A](a: A, fa: Option[A]): Option[A] = Some(a) override def appendK[A](fa: Option[A], a: A): Option[A] = if (fa.isDefined) fa else Some(a) From 2e5367a271eef77e81cbdba75276e52cef7aed5e Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 12:07:20 +0000 Subject: [PATCH 20/33] Optimize combineAllOptionK for sequence types --- core/src/main/scala/cats/instances/list.scala | 3 ++- .../src/main/scala/cats/instances/package.scala | 17 +++++++++++++++++ core/src/main/scala/cats/instances/queue.scala | 3 ++- core/src/main/scala/cats/instances/seq.scala | 3 ++- core/src/main/scala/cats/instances/vector.scala | 3 ++- 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 core/src/main/scala/cats/instances/package.scala diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 203b34b02f..87c7b19f87 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.{Chain, ZipList} +import cats.instances.instances.appendAll import cats.kernel.compat.scalaVersionSpecific._ import cats.kernel.instances.StaticMethods.wrapMutableIndexedSeq import cats.syntax.show._ @@ -22,7 +23,7 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toList) + if (iter.isEmpty) None else Some(appendAll(as, List.newBuilder[A]).result()) } override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala new file mode 100644 index 0000000000..a217fa4d5f --- /dev/null +++ b/core/src/main/scala/cats/instances/package.scala @@ -0,0 +1,17 @@ +package cats.instances + +import cats.kernel.compat.scalaVersionSpecific._ +import scala.collection.mutable.Builder + +@suppressUnusedImportWarningForScalaVersionSpecific +private[instances] object instances { + + def appendAll[F <: Iterable[A], A](as: IterableOnce[F], bldr: Builder[A, F]): bldr.type = { + val it = as.iterator + while (it.hasNext) { + bldr ++= it.next() + } + bldr + } + +} diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index a5af704bff..00b83fc339 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.Chain +import cats.instances.instances.appendAll import cats.kernel.compat.scalaVersionSpecific._ import cats.kernel.instances.StaticMethods.wrapMutableIndexedSeq import cats.syntax.show._ @@ -20,7 +21,7 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { override def combineAllOptionK[A](as: IterableOnce[Queue[A]]): Option[Queue[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).to(Queue)) + if (iter.isEmpty) None else Some(appendAll(as, Queue.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index e9d0b4274a..333f17becc 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.{Chain, ZipSeq} +import cats.instances.instances.appendAll import cats.kernel.compat.scalaVersionSpecific._ import scala.annotation.tailrec @@ -21,7 +22,7 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { override def combineAllOptionK[A](as: IterableOnce[Seq[A]]): Option[Seq[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toSeq) + if (iter.isEmpty) None else Some(appendAll(as, Seq.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index ad5bbea436..6bc8e106d6 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -2,6 +2,7 @@ package cats package instances import cats.data.{Chain, ZipVector} +import cats.instances.instances.appendAll import cats.kernel.compat.scalaVersionSpecific._ import cats.syntax.show._ @@ -21,7 +22,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { override def combineAllOptionK[A](as: IterableOnce[Vector[A]]): Option[Vector[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(iter.flatMap(_.iterator).toVector) + if (iter.isEmpty) None else Some(appendAll(as, Vector.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa From bcc07d88979388b020f1c023eca32cbef52dd76b Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 12:10:20 +0000 Subject: [PATCH 21/33] Eliminate unnecessary iterator allocation --- core/src/main/scala/cats/instances/list.scala | 2 +- core/src/main/scala/cats/instances/package.scala | 5 +---- core/src/main/scala/cats/instances/queue.scala | 2 +- core/src/main/scala/cats/instances/seq.scala | 2 +- core/src/main/scala/cats/instances/vector.scala | 2 +- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 87c7b19f87..39857ed220 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -23,7 +23,7 @@ trait ListInstances extends cats.kernel.instances.ListInstances { override def combineAllOptionK[A](as: IterableOnce[List[A]]): Option[List[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(as, List.newBuilder[A]).result()) + if (iter.isEmpty) None else Some(appendAll(iter, List.newBuilder[A]).result()) } override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa diff --git a/core/src/main/scala/cats/instances/package.scala b/core/src/main/scala/cats/instances/package.scala index a217fa4d5f..97ddada58b 100644 --- a/core/src/main/scala/cats/instances/package.scala +++ b/core/src/main/scala/cats/instances/package.scala @@ -1,13 +1,10 @@ package cats.instances -import cats.kernel.compat.scalaVersionSpecific._ import scala.collection.mutable.Builder -@suppressUnusedImportWarningForScalaVersionSpecific private[instances] object instances { - def appendAll[F <: Iterable[A], A](as: IterableOnce[F], bldr: Builder[A, F]): bldr.type = { - val it = as.iterator + def appendAll[F <: Iterable[A], A](it: Iterator[F], bldr: Builder[A, F]): bldr.type = { while (it.hasNext) { bldr ++= it.next() } diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 00b83fc339..9c58941ccd 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -21,7 +21,7 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { override def combineAllOptionK[A](as: IterableOnce[Queue[A]]): Option[Queue[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(as, Queue.newBuilder[A]).result()) + if (iter.isEmpty) None else Some(appendAll(iter, Queue.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index 333f17becc..f2f0219bcf 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -22,7 +22,7 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { override def combineAllOptionK[A](as: IterableOnce[Seq[A]]): Option[Seq[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(as, Seq.newBuilder[A]).result()) + if (iter.isEmpty) None else Some(appendAll(iter, Seq.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 6bc8e106d6..8bf94eb21a 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -22,7 +22,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { override def combineAllOptionK[A](as: IterableOnce[Vector[A]]): Option[Vector[A]] = { val iter = as.iterator - if (iter.isEmpty) None else Some(appendAll(as, Vector.newBuilder[A]).result()) + if (iter.isEmpty) None else Some(appendAll(iter, Vector.newBuilder[A]).result()) } override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa From e350e706f3867669309b7cd77ed51bf40465e415 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 12:13:11 +0000 Subject: [PATCH 22/33] Add Alternative#fromIterableOnce and Alternative#fromFoldable --- core/src/main/scala/cats/Alternative.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/scala/cats/Alternative.scala b/core/src/main/scala/cats/Alternative.scala index fc875d9af7..ca4d6970cb 100644 --- a/core/src/main/scala/cats/Alternative.scala +++ b/core/src/main/scala/cats/Alternative.scala @@ -1,6 +1,7 @@ package cats import simulacrum.typeclass +import cats.kernel.compat.scalaVersionSpecific._ @typeclass trait Alternative[F[_]] extends NonEmptyAlternative[F] with MonoidK[F] { self => @@ -99,8 +100,15 @@ import simulacrum.typeclass val F = self val G = Applicative[G] } + + def fromIterableOnce[A](as: IterableOnce[A]): F[A] = + combineAllK(as.iterator.map(pure(_))) + + def fromFoldable[G[_]: Foldable, A](as: G[A]): F[A] = + fromIterableOnce(Foldable[G].toIterable(as)) } +@suppressUnusedImportWarningForScalaVersionSpecific object Alternative { /* ======================================================================== */ From fee06252b8fde3fed8fcbaf7cd7a31dcec61e54d Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 13 Jan 2022 12:48:04 +0000 Subject: [PATCH 23/33] Argh scalafmt --- core/src/main/scala/cats/MonoidK.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/MonoidK.scala b/core/src/main/scala/cats/MonoidK.scala index 9503f69e9c..43259465f4 100644 --- a/core/src/main/scala/cats/MonoidK.scala +++ b/core/src/main/scala/cats/MonoidK.scala @@ -118,7 +118,7 @@ import simulacrum.typeclass def combineAllK[A](as: IterableOnce[F[A]]): F[A] = combineAllOptionK(as) match { case Some(fa) => fa - case None => empty[A] + case None => empty[A] } override def reverse: MonoidK[F] = From 6b5f7fa21544bc3aacf9c3efe764442f0c34bbe5 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 16:09:43 +0000 Subject: [PATCH 24/33] Law for Alterantive#fromIterableOnce plus optimized implementations --- core/src/main/scala-2.13+/cats/instances/arraySeq.scala | 2 ++ core/src/main/scala-2.13+/cats/instances/lazyList.scala | 4 ++++ core/src/main/scala-2.13+/cats/instances/stream.scala | 4 ++++ core/src/main/scala/cats/data/Chain.scala | 3 +++ core/src/main/scala/cats/instances/list.scala | 2 ++ core/src/main/scala/cats/instances/option.scala | 5 +++++ core/src/main/scala/cats/instances/queue.scala | 2 ++ core/src/main/scala/cats/instances/seq.scala | 2 ++ core/src/main/scala/cats/instances/vector.scala | 3 +++ laws/src/main/scala/cats/laws/AlternativeLaws.scala | 6 ++++++ .../main/scala/cats/laws/discipline/AlternativeTests.scala | 4 +++- laws/src/main/scala/cats/laws/discipline/arbitrary.scala | 3 +++ tests/src/test/scala/cats/tests/AlternativeSuite.scala | 1 + tests/src/test/scala/cats/tests/QueueSuite.scala | 1 + 14 files changed, 41 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala-2.13+/cats/instances/arraySeq.scala b/core/src/main/scala-2.13+/cats/instances/arraySeq.scala index 0047ab7e98..fbe833eafb 100644 --- a/core/src/main/scala-2.13+/cats/instances/arraySeq.scala +++ b/core/src/main/scala-2.13+/cats/instances/arraySeq.scala @@ -34,6 +34,8 @@ private[cats] object ArraySeqInstances { def combineK[A](xs: ArraySeq[A], ys: ArraySeq[A]): ArraySeq[A] = xs.concat(ys) + override def fromIterableOnce[A](as: IterableOnce[A]): ArraySeq[A] = ArraySeq.untagged.from(as) + override def prependK[A](a: A, fa: ArraySeq[A]): ArraySeq[A] = fa.prepended(a) override def appendK[A](fa: ArraySeq[A], a: A): ArraySeq[A] = fa.appended(a) diff --git a/core/src/main/scala-2.13+/cats/instances/lazyList.scala b/core/src/main/scala-2.13+/cats/instances/lazyList.scala index 7b2796444b..7765731a6b 100644 --- a/core/src/main/scala-2.13+/cats/instances/lazyList.scala +++ b/core/src/main/scala-2.13+/cats/instances/lazyList.scala @@ -2,12 +2,14 @@ package cats package instances import cats.kernel +import cats.kernel.compat.scalaVersionSpecific._ import cats.syntax.show._ import cats.data.Ior import cats.data.ZipLazyList import scala.annotation.tailrec +@suppressUnusedImportWarningForScalaVersionSpecific trait LazyListInstances extends cats.kernel.instances.LazyListInstances { implicit val catsStdInstancesForLazyList @@ -22,6 +24,8 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { def combineK[A](x: LazyList[A], y: LazyList[A]): LazyList[A] = x.lazyAppendedAll(y) + override def fromIterableOnce[A](as: IterableOnce[A]): LazyList[A] = LazyList.from(as) + override def prependK[A](a: A, fa: LazyList[A]): LazyList[A] = fa.prepended(a) override def appendK[A](fa: LazyList[A], a: A): LazyList[A] = fa.appended(a) diff --git a/core/src/main/scala-2.13+/cats/instances/stream.scala b/core/src/main/scala-2.13+/cats/instances/stream.scala index f835f92462..269543e9df 100644 --- a/core/src/main/scala-2.13+/cats/instances/stream.scala +++ b/core/src/main/scala-2.13+/cats/instances/stream.scala @@ -2,10 +2,12 @@ package cats package instances import cats.data.{Ior, ZipStream} +import cats.kernel.compat.scalaVersionSpecific._ import cats.syntax.show._ import scala.annotation.tailrec +@suppressUnusedImportWarningForScalaVersionSpecific trait StreamInstances extends cats.kernel.instances.StreamInstances { @deprecated("Use cats.instances.lazyList", "2.0.0-RC2") @@ -17,6 +19,8 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y + override def fromIterableOnce[A](as: IterableOnce[A]): Stream[A] = Stream.from(as) + override def prependK[A](a: A, fa: Stream[A]): Stream[A] = a #:: fa def pure[A](x: A): Stream[A] = Stream(x) diff --git a/core/src/main/scala/cats/data/Chain.scala b/core/src/main/scala/cats/data/Chain.scala index b1decfdaa4..b81b3d699c 100644 --- a/core/src/main/scala/cats/data/Chain.scala +++ b/core/src/main/scala/cats/data/Chain.scala @@ -3,6 +3,7 @@ package data import Chain._ import cats.kernel.instances.StaticMethods +import cats.kernel.compat.scalaVersionSpecific._ import scala.annotation.tailrec import scala.collection.immutable.{IndexedSeq => ImIndexedSeq, SortedMap, TreeSet} @@ -678,6 +679,7 @@ sealed abstract class Chain[+A] { } } +@suppressUnusedImportWarningForScalaVersionSpecific object Chain extends ChainInstances { private val sentinel: Function1[Any, Any] = new scala.runtime.AbstractFunction1[Any, Any] { def apply(a: Any) = this } @@ -1029,6 +1031,7 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 { def empty[A]: Chain[A] = Chain.nil def combineK[A](c: Chain[A], c2: Chain[A]): Chain[A] = Chain.concat(c, c2) + override def fromIterableOnce[A](as: IterableOnce[A]): Chain[A] = Chain.fromSeq(as.iterator.toSeq) def pure[A](a: A): Chain[A] = Chain.one(a) def flatMap[A, B](fa: Chain[A])(f: A => Chain[B]): Chain[B] = fa.flatMap(f) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 39857ed220..0d89038b9d 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -26,6 +26,8 @@ trait ListInstances extends cats.kernel.instances.ListInstances { if (iter.isEmpty) None else Some(appendAll(iter, List.newBuilder[A]).result()) } + override def fromIterableOnce[A](as: IterableOnce[A]): List[A] = List.from(as) + override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa override def appendK[A](fa: List[A], a: A): List[A] = fa :+ a diff --git a/core/src/main/scala/cats/instances/option.scala b/core/src/main/scala/cats/instances/option.scala index 1c92c211f5..a2761dec93 100644 --- a/core/src/main/scala/cats/instances/option.scala +++ b/core/src/main/scala/cats/instances/option.scala @@ -40,6 +40,11 @@ trait OptionInstances extends cats.kernel.instances.OptionInstances { } } + override def fromIterableOnce[A](as: IterableOnce[A]): Option[A] = { + val iter = as.iterator + if (iter.hasNext) Some(iter.next()) else None + } + override def prependK[A](a: A, fa: Option[A]): Option[A] = Some(a) override def appendK[A](fa: Option[A], a: A): Option[A] = if (fa.isDefined) fa else Some(a) diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 9c58941ccd..991ca4796a 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -24,6 +24,8 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { if (iter.isEmpty) None else Some(appendAll(iter, Queue.newBuilder[A]).result()) } + override def fromIterableOnce[A](as: IterableOnce[A]): Queue[A] = Queue.from(as) + override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa override def appendK[A](fa: Queue[A], a: A): Queue[A] = fa.enqueue(a) diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index f2f0219bcf..bae1094611 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -25,6 +25,8 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { if (iter.isEmpty) None else Some(appendAll(iter, Seq.newBuilder[A]).result()) } + override def fromIterableOnce[A](as: IterableOnce[A]): Seq[A] = Seq.from(as) + override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa override def appendK[A](fa: Seq[A], a: A): Seq[A] = fa :+ a diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 8bf94eb21a..0d284ccf2d 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -25,6 +25,9 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { if (iter.isEmpty) None else Some(appendAll(iter, Vector.newBuilder[A]).result()) } + override def fromIterableOnce[A](as: IterableOnce[A]): Vector[A] = + Vector.from(as) + override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa override def appendK[A](fa: Vector[A], a: A): Vector[A] = fa :+ a diff --git a/laws/src/main/scala/cats/laws/AlternativeLaws.scala b/laws/src/main/scala/cats/laws/AlternativeLaws.scala index 06b103f634..15100eed89 100644 --- a/laws/src/main/scala/cats/laws/AlternativeLaws.scala +++ b/laws/src/main/scala/cats/laws/AlternativeLaws.scala @@ -2,6 +2,7 @@ package cats package laws import cats.syntax.all._ +import cats.kernel.compat.scalaVersionSpecific._ trait AlternativeLaws[F[_]] extends NonEmptyAlternativeLaws[F] with MonoidKLaws[F] { implicit override def F: Alternative[F] @@ -17,8 +18,13 @@ trait AlternativeLaws[F[_]] extends NonEmptyAlternativeLaws[F] with MonoidKLaws[ // Perhaps should be deprecated in favor of nonEmptyAlternativeRightDistributivity def alternativeRightDistributivity[A, B](fa: F[A], ff: F[A => B], fg: F[A => B]): IsEq[F[B]] = nonEmptyAlternativeRightDistributivity(fa, ff, fg) + + def fromIterableOnce[A](as: Iterable[A]): IsEq[F[A]] = + F.fromIterableOnce(as) <-> F.combineAllK(as.iterator.map(F.pure(_))) + } +@suppressUnusedImportWarningForScalaVersionSpecific object AlternativeLaws { def apply[F[_]](implicit ev: Alternative[F]): AlternativeLaws[F] = new AlternativeLaws[F] { def F: Alternative[F] = ev } diff --git a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala index 7fc610ac00..3c2d0b3db3 100644 --- a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala @@ -15,6 +15,7 @@ trait AlternativeTests[F[_]] extends NonEmptyAlternativeTests[F] with MonoidKTes ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], + ArbIterableOnce: Arbitrary[IterableOnce[A]], CogenA: Cogen[A], CogenB: Cogen[B], CogenC: Cogen[C], @@ -29,7 +30,8 @@ trait AlternativeTests[F[_]] extends NonEmptyAlternativeTests[F] with MonoidKTes val bases: Seq[(String, RuleSet)] = Nil val parents: Seq[RuleSet] = Seq(monoidK[A], nonEmptyAlternative[A, B, C]) val props: Seq[(String, Prop)] = Seq( - "right absorption" -> forAll(laws.alternativeRightAbsorption[A, B] _) + "right absorption" -> forAll(laws.alternativeRightAbsorption[A, B] _), + "fromIterableOnce" -> forAll(laws.fromIterableOnce[A] _) ) } } diff --git a/laws/src/main/scala/cats/laws/discipline/arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala index 43b9752137..bc053adb45 100644 --- a/laws/src/main/scala/cats/laws/discipline/arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala @@ -398,6 +398,9 @@ object arbitrary extends ArbitraryInstances0 with ScalaVersionSpecific.Arbitrary sealed private[discipline] trait ArbitraryInstances0 { + implicit def catsLawsArbitraryForIterableOnce[A](implicit AA: Arbitrary[A]): Arbitrary[IterableOnce[A]] = + Arbitrary(getArbitrary[List[A]]) + implicit def catsLawArbitraryForIndexedStateT[F[_], SA, SB, A](implicit F: Arbitrary[F[SA => F[(SB, A)]]] ): Arbitrary[IndexedStateT[F, SA, SB, A]] = diff --git a/tests/src/test/scala/cats/tests/AlternativeSuite.scala b/tests/src/test/scala/cats/tests/AlternativeSuite.scala index d715c7f5f7..49d0d79143 100644 --- a/tests/src/test/scala/cats/tests/AlternativeSuite.scala +++ b/tests/src/test/scala/cats/tests/AlternativeSuite.scala @@ -3,6 +3,7 @@ package cats.tests import cats.Alternative import cats.FlatMap import cats.laws.discipline.AlternativeTests +import cats.laws.discipline.arbitrary._ import cats.syntax.eq._ import org.scalacheck.Prop._ diff --git a/tests/src/test/scala/cats/tests/QueueSuite.scala b/tests/src/test/scala/cats/tests/QueueSuite.scala index 28ac4c6660..8e1f82ef0a 100644 --- a/tests/src/test/scala/cats/tests/QueueSuite.scala +++ b/tests/src/test/scala/cats/tests/QueueSuite.scala @@ -11,6 +11,7 @@ import cats.laws.discipline.{ TraverseFilterTests, TraverseTests } +import cats.laws.discipline.arbitrary._ import cats.syntax.show._ import scala.collection.immutable.Queue import cats.syntax.eq._ From b531693bd702a684050cb5eff89b3bb0c7c8bace Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 16:30:06 +0000 Subject: [PATCH 25/33] Argh scala 2.12 - why do you do this to me? :( --- core/src/main/scala-2.12/cats/instances/stream.scala | 4 ++++ core/src/main/scala/cats/instances/list.scala | 2 +- core/src/main/scala/cats/instances/queue.scala | 9 ++++++++- core/src/main/scala/cats/instances/seq.scala | 2 +- core/src/main/scala/cats/instances/vector.scala | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala-2.12/cats/instances/stream.scala b/core/src/main/scala-2.12/cats/instances/stream.scala index 460557acf9..a802909235 100644 --- a/core/src/main/scala-2.12/cats/instances/stream.scala +++ b/core/src/main/scala-2.12/cats/instances/stream.scala @@ -2,10 +2,12 @@ package cats package instances import cats.data.{Ior, ZipStream} +import cats.kernel.compat.scalaVersionSpecific._ import cats.syntax.show._ import scala.annotation.tailrec +@suppressUnusedImportWarningForScalaVersionSpecific trait StreamInstances extends cats.kernel.instances.StreamInstances { implicit val catsStdInstancesForStream @@ -16,6 +18,8 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y + override def fromIterableOnce[A](as: IterableOnce[A]): Stream[A] = as.iterator.to(LazyList) + override def prependK[A](a: A, fa: Stream[A]): Stream[A] = a #:: fa def pure[A](x: A): Stream[A] = Stream(x) diff --git a/core/src/main/scala/cats/instances/list.scala b/core/src/main/scala/cats/instances/list.scala index 0d89038b9d..cdff56d6dc 100644 --- a/core/src/main/scala/cats/instances/list.scala +++ b/core/src/main/scala/cats/instances/list.scala @@ -26,7 +26,7 @@ trait ListInstances extends cats.kernel.instances.ListInstances { if (iter.isEmpty) None else Some(appendAll(iter, List.newBuilder[A]).result()) } - override def fromIterableOnce[A](as: IterableOnce[A]): List[A] = List.from(as) + override def fromIterableOnce[A](as: IterableOnce[A]): List[A] = as.iterator.toList override def prependK[A](a: A, fa: List[A]): List[A] = a :: fa diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 991ca4796a..ed3657056d 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -24,7 +24,14 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { if (iter.isEmpty) None else Some(appendAll(iter, Queue.newBuilder[A]).result()) } - override def fromIterableOnce[A](as: IterableOnce[A]): Queue[A] = Queue.from(as) + override def fromIterableOnce[A](as: IterableOnce[A]): Queue[A] = { + val iter = as.iterator + val builder = Queue.newBuilder[A] + while (iter.hasNext) { + builder.addOne(iter.next()) + } + builder.result() + } override def prependK[A](a: A, fa: Queue[A]): Queue[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index bae1094611..6343b589c9 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -25,7 +25,7 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { if (iter.isEmpty) None else Some(appendAll(iter, Seq.newBuilder[A]).result()) } - override def fromIterableOnce[A](as: IterableOnce[A]): Seq[A] = Seq.from(as) + override def fromIterableOnce[A](as: IterableOnce[A]): Seq[A] = as.iterator.toSeq override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa diff --git a/core/src/main/scala/cats/instances/vector.scala b/core/src/main/scala/cats/instances/vector.scala index 0d284ccf2d..085a24ee51 100644 --- a/core/src/main/scala/cats/instances/vector.scala +++ b/core/src/main/scala/cats/instances/vector.scala @@ -26,7 +26,7 @@ trait VectorInstances extends cats.kernel.instances.VectorInstances { } override def fromIterableOnce[A](as: IterableOnce[A]): Vector[A] = - Vector.from(as) + as.iterator.toVector override def prependK[A](a: A, fa: Vector[A]): Vector[A] = a +: fa From f5bdeca41cbf26c00bb4165bbc5bf1e41b8d8651 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 16:33:51 +0000 Subject: [PATCH 26/33] Use method not deprecated in 2.12 --- core/src/main/scala-2.12/cats/instances/stream.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala-2.12/cats/instances/stream.scala b/core/src/main/scala-2.12/cats/instances/stream.scala index a802909235..c494b94486 100644 --- a/core/src/main/scala-2.12/cats/instances/stream.scala +++ b/core/src/main/scala-2.12/cats/instances/stream.scala @@ -18,7 +18,7 @@ trait StreamInstances extends cats.kernel.instances.StreamInstances { def combineK[A](x: Stream[A], y: Stream[A]): Stream[A] = x #::: y - override def fromIterableOnce[A](as: IterableOnce[A]): Stream[A] = as.iterator.to(LazyList) + override def fromIterableOnce[A](as: IterableOnce[A]): Stream[A] = as.iterator.toStream override def prependK[A](a: A, fa: Stream[A]): Stream[A] = a #:: fa From 7a0cd8131ce4dca90d3c284bdb1f826109b58016 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 16:38:59 +0000 Subject: [PATCH 27/33] More efficient LazyList combineAllOptionK which preserves laziness --- core/src/main/scala-2.13+/cats/instances/lazyList.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/scala-2.13+/cats/instances/lazyList.scala b/core/src/main/scala-2.13+/cats/instances/lazyList.scala index 7765731a6b..fa6ed7f8cd 100644 --- a/core/src/main/scala-2.13+/cats/instances/lazyList.scala +++ b/core/src/main/scala-2.13+/cats/instances/lazyList.scala @@ -24,6 +24,11 @@ trait LazyListInstances extends cats.kernel.instances.LazyListInstances { def combineK[A](x: LazyList[A], y: LazyList[A]): LazyList[A] = x.lazyAppendedAll(y) + override def combineAllOptionK[A](as: IterableOnce[LazyList[A]]): Option[LazyList[A]] = { + val iter = as.iterator + if (iter.isEmpty) None else Some(LazyList.from(iter.flatMap(_.iterator))) + } + override def fromIterableOnce[A](as: IterableOnce[A]): LazyList[A] = LazyList.from(as) override def prependK[A](a: A, fa: LazyList[A]): LazyList[A] = fa.prepended(a) From 361b6c8e68c6112d3922717063a9e1851ea0fdab Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 16:41:11 +0000 Subject: [PATCH 28/33] Make Alternative#fromFoldable final Binary compatibility prohibits us from laws testing it as we would have to add another type parameter to `AlternativeLaws` so we mark it final instead --- core/src/main/scala/cats/Alternative.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/Alternative.scala b/core/src/main/scala/cats/Alternative.scala index ca4d6970cb..a94821c0cc 100644 --- a/core/src/main/scala/cats/Alternative.scala +++ b/core/src/main/scala/cats/Alternative.scala @@ -104,7 +104,7 @@ import cats.kernel.compat.scalaVersionSpecific._ def fromIterableOnce[A](as: IterableOnce[A]): F[A] = combineAllK(as.iterator.map(pure(_))) - def fromFoldable[G[_]: Foldable, A](as: G[A]): F[A] = + final def fromFoldable[G[_]: Foldable, A](as: G[A]): F[A] = fromIterableOnce(Foldable[G].toIterable(as)) } From 12053e195d199236c87f0b627a8c1830a9a33207 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 17:17:27 +0000 Subject: [PATCH 29/33] Remove unnecessary Arbitrary instance and fix 2.12 issues --- core/src/main/scala/cats/instances/queue.scala | 5 +---- core/src/main/scala/cats/instances/seq.scala | 6 +++++- .../main/scala/cats/laws/discipline/AlternativeTests.scala | 2 +- laws/src/main/scala/cats/laws/discipline/arbitrary.scala | 3 --- tests/src/test/scala/cats/tests/AlternativeSuite.scala | 1 - 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index ed3657056d..75165e6be9 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -25,11 +25,8 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { } override def fromIterableOnce[A](as: IterableOnce[A]): Queue[A] = { - val iter = as.iterator val builder = Queue.newBuilder[A] - while (iter.hasNext) { - builder.addOne(iter.next()) - } + builder ++= as.iterator builder.result() } diff --git a/core/src/main/scala/cats/instances/seq.scala b/core/src/main/scala/cats/instances/seq.scala index 6343b589c9..d02528306c 100644 --- a/core/src/main/scala/cats/instances/seq.scala +++ b/core/src/main/scala/cats/instances/seq.scala @@ -25,7 +25,11 @@ trait SeqInstances extends cats.kernel.instances.SeqInstances { if (iter.isEmpty) None else Some(appendAll(iter, Seq.newBuilder[A]).result()) } - override def fromIterableOnce[A](as: IterableOnce[A]): Seq[A] = as.iterator.toSeq + override def fromIterableOnce[A](as: IterableOnce[A]): Seq[A] = { + val builder = Seq.newBuilder[A] + builder ++= as + builder.result() + } override def prependK[A](a: A, fa: Seq[A]): Seq[A] = a +: fa diff --git a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala index 3c2d0b3db3..6ef4913b48 100644 --- a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala @@ -15,7 +15,7 @@ trait AlternativeTests[F[_]] extends NonEmptyAlternativeTests[F] with MonoidKTes ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], - ArbIterableOnce: Arbitrary[IterableOnce[A]], + ArbIterableOnce: Arbitrary[Iterable[A]], CogenA: Cogen[A], CogenB: Cogen[B], CogenC: Cogen[C], diff --git a/laws/src/main/scala/cats/laws/discipline/arbitrary.scala b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala index bc053adb45..43b9752137 100644 --- a/laws/src/main/scala/cats/laws/discipline/arbitrary.scala +++ b/laws/src/main/scala/cats/laws/discipline/arbitrary.scala @@ -398,9 +398,6 @@ object arbitrary extends ArbitraryInstances0 with ScalaVersionSpecific.Arbitrary sealed private[discipline] trait ArbitraryInstances0 { - implicit def catsLawsArbitraryForIterableOnce[A](implicit AA: Arbitrary[A]): Arbitrary[IterableOnce[A]] = - Arbitrary(getArbitrary[List[A]]) - implicit def catsLawArbitraryForIndexedStateT[F[_], SA, SB, A](implicit F: Arbitrary[F[SA => F[(SB, A)]]] ): Arbitrary[IndexedStateT[F, SA, SB, A]] = diff --git a/tests/src/test/scala/cats/tests/AlternativeSuite.scala b/tests/src/test/scala/cats/tests/AlternativeSuite.scala index 49d0d79143..d715c7f5f7 100644 --- a/tests/src/test/scala/cats/tests/AlternativeSuite.scala +++ b/tests/src/test/scala/cats/tests/AlternativeSuite.scala @@ -3,7 +3,6 @@ package cats.tests import cats.Alternative import cats.FlatMap import cats.laws.discipline.AlternativeTests -import cats.laws.discipline.arbitrary._ import cats.syntax.eq._ import org.scalacheck.Prop._ From a02f41edab922f611464086c4bc770a2fe3ef479 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 28 Jan 2022 17:33:02 +0000 Subject: [PATCH 30/33] Remove unncessary and binary incompatible added implicit param --- laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala index 6ef4913b48..3574daf967 100644 --- a/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala +++ b/laws/src/main/scala/cats/laws/discipline/AlternativeTests.scala @@ -15,7 +15,6 @@ trait AlternativeTests[F[_]] extends NonEmptyAlternativeTests[F] with MonoidKTes ArbFC: Arbitrary[F[C]], ArbFAtoB: Arbitrary[F[A => B]], ArbFBtoC: Arbitrary[F[B => C]], - ArbIterableOnce: Arbitrary[Iterable[A]], CogenA: Cogen[A], CogenB: Cogen[B], CogenC: Cogen[C], From 19b333833990abf96327dac38583636b33290341 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 24 Feb 2022 11:45:44 +0000 Subject: [PATCH 31/33] I really don't think there is a cyclic dependency dotty... --- core/src/main/scala/cats/data/Chain.scala | 19 +++++++++++++++++-- .../main/scala/cats/laws/SemigroupKLaws.scala | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/data/Chain.scala b/core/src/main/scala/cats/data/Chain.scala index b81b3d699c..17221cb55c 100644 --- a/core/src/main/scala/cats/data/Chain.scala +++ b/core/src/main/scala/cats/data/Chain.scala @@ -1,7 +1,22 @@ package cats package data -import Chain._ +import Chain.{ + empty, + fromSeq, + nil, + one, + sentinel, + traverseFilterViaChain, + traverseViaChain, + Append, + ChainIterator, + ChainReverseIterator, + Empty, + NonEmpty, + Singleton, + Wrap +} import cats.kernel.instances.StaticMethods import cats.kernel.compat.scalaVersionSpecific._ @@ -1031,7 +1046,7 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 { def empty[A]: Chain[A] = Chain.nil def combineK[A](c: Chain[A], c2: Chain[A]): Chain[A] = Chain.concat(c, c2) - override def fromIterableOnce[A](as: IterableOnce[A]): Chain[A] = Chain.fromSeq(as.iterator.toSeq) + override def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = Chain.fromSeq(xs.iterator.toSeq) def pure[A](a: A): Chain[A] = Chain.one(a) def flatMap[A, B](fa: Chain[A])(f: A => Chain[B]): Chain[B] = fa.flatMap(f) diff --git a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala index 3d3755ba4a..d743ccc2ff 100644 --- a/laws/src/main/scala/cats/laws/SemigroupKLaws.scala +++ b/laws/src/main/scala/cats/laws/SemigroupKLaws.scala @@ -13,7 +13,7 @@ trait SemigroupKLaws[F[_]] { def repeat1K[A](a: F[A]): IsEq[F[A]] = F.combineNK(a, 1) <-> a - def repeat2[A](a: F[A]): IsEq[F[A]] = + def repeat2K[A](a: F[A]): IsEq[F[A]] = F.combineNK(a, 2) <-> F.combineK(a, a) def combineAllOptionK[A](xs: Vector[F[A]]): IsEq[Option[F[A]]] = From 549a9f0abc60707f1576ef6a7d64c6cb6ed33a7e Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Thu, 3 Mar 2022 16:21:49 +0000 Subject: [PATCH 32/33] Avoid unnecessary allocation --- core/src/main/scala/cats/instances/queue.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/instances/queue.scala b/core/src/main/scala/cats/instances/queue.scala index 75165e6be9..6333566193 100644 --- a/core/src/main/scala/cats/instances/queue.scala +++ b/core/src/main/scala/cats/instances/queue.scala @@ -26,7 +26,7 @@ trait QueueInstances extends cats.kernel.instances.QueueInstances { override def fromIterableOnce[A](as: IterableOnce[A]): Queue[A] = { val builder = Queue.newBuilder[A] - builder ++= as.iterator + builder ++= as builder.result() } From a744378d03a01987b93f3e57c3701e590326897e Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Sat, 12 Mar 2022 14:30:24 +0000 Subject: [PATCH 33/33] Optimize fromIterableOnce for Chain --- core/src/main/scala/cats/data/Chain.scala | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/cats/data/Chain.scala b/core/src/main/scala/cats/data/Chain.scala index 17221cb55c..3663edd912 100644 --- a/core/src/main/scala/cats/data/Chain.scala +++ b/core/src/main/scala/cats/data/Chain.scala @@ -782,6 +782,18 @@ object Chain extends ChainInstances { else if (s.lengthCompare(1) == 0) one(s.head) else Wrap(s) + /** + * Creates a Chain from the specified IterableOnce. + */ + def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = + xs match { + case s: Seq[A @unchecked] => + // Seq is a subclass of IterableOnce, so the type has to be compatible + Chain.fromSeq(s) // pay O(1) not O(N) cost + case notSeq => + Chain.fromSeq(notSeq.iterator.toSeq) + } + /** * Creates a Chain from the specified elements. */ @@ -1046,7 +1058,7 @@ sealed abstract private[data] class ChainInstances extends ChainInstances1 { def empty[A]: Chain[A] = Chain.nil def combineK[A](c: Chain[A], c2: Chain[A]): Chain[A] = Chain.concat(c, c2) - override def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = Chain.fromSeq(xs.iterator.toSeq) + override def fromIterableOnce[A](xs: IterableOnce[A]): Chain[A] = Chain.fromIterableOnce(xs) def pure[A](a: A): Chain[A] = Chain.one(a) def flatMap[A, B](fa: Chain[A])(f: A => Chain[B]): Chain[B] = fa.flatMap(f)