From 6cca3981113fdd1d9796fab0f8d8fd7d9b3d13d3 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 20 May 2022 16:40:58 +0100 Subject: [PATCH 1/8] Port SemigroupK and MonoidK to new derivation scheme --- .../scala-3/cats/derived/DerivedMonoidK.scala | 27 +++++++++++++++++++ .../cats/derived/DerivedSemigroupK.scala | 24 +++++++++++++++++ .../main/scala-3/cats/derived/monoidk.scala | 12 --------- .../main/scala-3/cats/derived/package.scala | 12 ++++----- .../scala-3/cats/derived/semigroupk.scala | 14 ---------- .../scala-3/cats/derived/MonoidKTests.scala | 2 +- .../cats/derived/SemigroupKTests.scala | 3 +-- 7 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/DerivedMonoidK.scala create mode 100644 core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala delete mode 100644 core/src/main/scala-3/cats/derived/monoidk.scala delete mode 100644 core/src/main/scala-3/cats/derived/semigroupk.scala diff --git a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala new file mode 100644 index 00000000..9b8925f6 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala @@ -0,0 +1,27 @@ +package cats.derived + +import cats.{Monoid, MonoidK} +import shapeless3.deriving.{Const, K1} + +import scala.compiletime.* + +type DerivedMonoidK[F[_]] = Derived[MonoidK[F]] +object DerivedMonoidK: + type Or[F[_]] = Derived.Or[MonoidK[F]] + inline def apply[F[_]]: MonoidK[F] = + import DerivedMonoidK.given + summonInline[DerivedMonoidK[F]].instance + + given [T](using T: Monoid[T]): DerivedMonoidK[Const[T]] = new MonoidK[Const[T]]: + final override def empty[A]: Const[T][A] = T.empty + + final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) + + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedMonoidK[F] = + given K1.ProductInstances[MonoidK, F] = inst.unify + new Product[MonoidK, F] {} + + trait Product[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstances[T, F]) + extends MonoidK[F], + DerivedSemigroupK.Product[T, F]: + final override def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A]) diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala new file mode 100644 index 00000000..c6b92342 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -0,0 +1,24 @@ +package cats.derived + +import cats.{Semigroup, SemigroupK} +import shapeless3.deriving.{Const, K1} + +import scala.compiletime.* + +type DerivedSemigroupK[F[_]] = Derived[SemigroupK[F]] +object DerivedSemigroupK: + type Or[F[_]] = Derived.Or[SemigroupK[F]] + inline def apply[F[_]]: SemigroupK[F] = + import DerivedSemigroupK.given + summonInline[DerivedSemigroupK[F]].instance + + given [T](using T: Semigroup[T]): DerivedSemigroupK[Const[T]] = new SemigroupK[Const[T]]: + final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) + + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = + given K1.ProductInstances[SemigroupK, F] = inst.unify + new Product[SemigroupK, F] {} + + trait Product[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F]) extends SemigroupK[F]: + final override def combineK[A](x: F[A], y: F[A]): F[A] = + inst.map2[A, A, A](x, y)([t[_]] => (smgrpk: T[t], x: t[A], y: t[A]) => smgrpk.combineK(x, y)) diff --git a/core/src/main/scala-3/cats/derived/monoidk.scala b/core/src/main/scala-3/cats/derived/monoidk.scala deleted file mode 100644 index cc5e878c..00000000 --- a/core/src/main/scala-3/cats/derived/monoidk.scala +++ /dev/null @@ -1,12 +0,0 @@ -package cats.derived - -import cats.{Monoid, MonoidK} -import shapeless3.deriving.{Const, K1} - -trait ProductMonoidK[T[x[_]] <: MonoidK[x], F[_]](using inst: K1.ProductInstances[T, F]) - extends ProductSemigroupK[T, F], - MonoidK[F]: - def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A]) - -trait MonoidKDerivation: - extension (F: MonoidK.type) inline def derived[F[_]](using gen: K1.Generic[F]): MonoidK[F] = ??? diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index a3add49d..debf0a67 100644 --- a/core/src/main/scala-3/cats/derived/package.scala +++ b/core/src/main/scala-3/cats/derived/package.scala @@ -24,14 +24,10 @@ extension (x: Functor.type) inline def derived[F[_]]: Functor[F] = DerivedFuncto extension (x: Reducible.type) inline def derived[F[_]]: Reducible[F] = DerivedReducible[F] extension (x: Traverse.type) inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F] extension (x: NonEmptyTraverse.type) inline def derived[F[_]]: NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F] +extension (x: SemigroupK.type) inline def derived[F[_]]: SemigroupK[F] = DerivedSemigroupK[F] +extension (x: MonoidK.type) inline def derived[F[_]]: MonoidK[F] = DerivedMonoidK[F] -object semiauto - extends ContravariantDerivation, - InvariantDerivation, - MonoidKDerivation, - PartialOrderDerivation, - SemigroupKDerivation, - Instances: +object semiauto extends ContravariantDerivation, InvariantDerivation, PartialOrderDerivation, Instances: inline def eq[A]: Eq[A] = DerivedEq[A] inline def hash[A]: Hash[A] = DerivedHash[A] @@ -51,6 +47,8 @@ object semiauto inline def traverse[F[_]]: Traverse[F] = DerivedTraverse[F] inline def nonEmptyTraverse[F[_]]: NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F] inline def show[A]: Show[A] = DerivedShow[A] + inline def semigroupK[F[_]]: SemigroupK[F] = DerivedSemigroupK[F] + inline def monoidK[F[_]]: MonoidK[F] = DerivedMonoidK[F] object auto: object eq: diff --git a/core/src/main/scala-3/cats/derived/semigroupk.scala b/core/src/main/scala-3/cats/derived/semigroupk.scala deleted file mode 100644 index c9b000f3..00000000 --- a/core/src/main/scala-3/cats/derived/semigroupk.scala +++ /dev/null @@ -1,14 +0,0 @@ -package cats.derived - -import cats.{Semigroup, SemigroupK} -import shapeless3.deriving.{Const, K1} - -trait ProductSemigroupK[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F]) extends SemigroupK[F]: - def combineK[A](x: F[A], y: F[A]): F[A] = inst.map2[A, A, A](x, y)( - [t[_]] => (smgrpk: T[t], t0: t[A], t1: t[A]) => smgrpk.combineK(t0, t1) - ) - -trait SemigroupKDerivation: - extension (F: SemigroupK.type) - inline def derived[F[_]](using gen: K1.ProductGeneric[F]): SemigroupK[F] = - new ProductSemigroupK[SemigroupK, F] {} diff --git a/core/src/test/scala-3/cats/derived/MonoidKTests.scala b/core/src/test/scala-3/cats/derived/MonoidKTests.scala index 80028a5d..e85a531e 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKTests.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKTests.scala @@ -2,7 +2,7 @@ package cats.derived import alleycats.* import cats.* -import cats.derived.semiauto.* +import cats.derived.* class MonoidKTests { // case class Foo[A](i: String, l: List[A]) derives MonoidK diff --git a/core/src/test/scala-3/cats/derived/SemigroupKTests.scala b/core/src/test/scala-3/cats/derived/SemigroupKTests.scala index e5030765..aa6b30d5 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKTests.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKTests.scala @@ -2,8 +2,7 @@ package cats.derived import alleycats.* import cats.* -import cats.derived.semiauto.* -import cats.derived.semiauto.given +import cats.derived.* class SemigroupKTests { // case class Foo[A](i: String, l: List[A]) derives SemigroupK From 415df3c052c160b3a12f29d25e00a3843f6e94ae Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 20 May 2022 17:19:31 +0100 Subject: [PATCH 2/8] WIP port of SemigroupK suite to scala 3 --- .../cats/derived/DerivedSemigroupK.scala | 4 ++ .../cats/derived/SemigroupKSuite.scala | 60 +++++++++++++++++++ .../cats/derived/SemigroupKTests.scala | 9 --- 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 core/src/test/scala-3/cats/derived/SemigroupKSuite.scala delete mode 100644 core/src/test/scala-3/cats/derived/SemigroupKTests.scala diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala index c6b92342..5a6826db 100644 --- a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -15,6 +15,10 @@ object DerivedSemigroupK: given [T](using T: Semigroup[T]): DerivedSemigroupK[Const[T]] = new SemigroupK[Const[T]]: final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = + given SemigroupK[G] = G.unify + F.unify.compose[G] + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = given K1.ProductInstances[SemigroupK, F] = inst.unify new Product[SemigroupK, F] {} diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala new file mode 100644 index 00000000..bcbf2ec5 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -0,0 +1,60 @@ +package cats.derived + +import cats.* +import cats.laws.discipline.{SemigroupKTests, SerializableTests} +import org.scalacheck.Arbitrary + +class SemigroupKSuite extends KittensSuite { + import SemigroupKSuite.* + import TestDefns.* + + def testSemigroupK(context: String)(implicit + // complexProduct: SemigroupK[ComplexProduct], + caseClassWOption: SemigroupK[CaseClassWOption], + boxMul: SemigroupK[BoxMul] + ): Unit = { + // checkAll(s"$context.SemigroupK[ComplexProduct]", SemigroupKTests[ComplexProduct].semigroupK[Char]) + checkAll(s"$context.SemigroupK[CaseClassWOption]", SemigroupKTests[CaseClassWOption].semigroupK[Char]) + checkAll(s"$context.SemigroupK[BoxMul]", SemigroupKTests[BoxMul].semigroupK[Char]) + // checkAll(s"$context.SemigroupK is Serializable", SerializableTests.serializable(SemigroupK[ComplexProduct])) + + test(s"$context.SemigroupK respects existing instances") { + assert(boxMul.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) + } + } + + // locally { + // import auto.semigroupK.given + // testSemigroupK("auto") + // } + + locally { + import semiInstances.given + testSemigroupK("semiauto") + } +} + +object SemigroupKSuite { + import TestDefns._ + + type BoxMul[A] = Box[Mul[A]] + + object semiInstances { + // implicit val complexProduct: SemigroupK[ComplexProduct] = semiauto.semigroupK + implicit val caseClassWOption: SemigroupK[CaseClassWOption] = semiauto.semigroupK + implicit val boxMul: SemigroupK[BoxMul] = semiauto.semigroupK + } + + final case class Mul[T](value: Int) + object Mul { + + implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) + + implicit def arbitrary[T]: Arbitrary[Mul[T]] = + Arbitrary(Arbitrary.arbitrary[Int].map(apply)) + + implicit val semigroupK: SemigroupK[Mul] = new SemigroupK[Mul] { + def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) + } + } +} diff --git a/core/src/test/scala-3/cats/derived/SemigroupKTests.scala b/core/src/test/scala-3/cats/derived/SemigroupKTests.scala deleted file mode 100644 index aa6b30d5..00000000 --- a/core/src/test/scala-3/cats/derived/SemigroupKTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class SemigroupKTests { // - case class Foo[A](i: String, l: List[A]) derives SemigroupK -} From 7f74610757953db6a9d876b357ebac1d53779dcb Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 20 May 2022 18:07:46 +0100 Subject: [PATCH 3/8] Nested derivations for SemigroupK --- .../cats/derived/DerivedSemigroupK.scala | 7 +++++-- .../src/main/scala-3/cats/derived/package.scala | 6 ++++++ .../scala-3/cats/derived/SemigroupKSuite.scala | 17 +++++++++-------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala index 5a6826db..aa2638b9 100644 --- a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -15,10 +15,13 @@ object DerivedSemigroupK: given [T](using T: Semigroup[T]): DerivedSemigroupK[Const[T]] = new SemigroupK[Const[T]]: final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) - given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = - given SemigroupK[G] = G.unify + given [F[_], G[_]](using F: Or[F]): DerivedSemigroupK[[x] =>> F[G[x]]] = F.unify.compose[G] + given [F[_], G[_]](using F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = + new SemigroupK[[x] =>> F[G[x]]]: + final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = given K1.ProductInstances[SemigroupK, F] = inst.unify new Product[SemigroupK, F] {} diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index debf0a67..2fa7c556 100644 --- a/core/src/main/scala-3/cats/derived/package.scala +++ b/core/src/main/scala-3/cats/derived/package.scala @@ -104,3 +104,9 @@ object auto: object nonEmptyTraverse: inline given [F[_]](using NotGiven[NonEmptyTraverse[F]]): NonEmptyTraverse[F] = DerivedNonEmptyTraverse[F] + + object semigroupK: + inline given [F[_]](using NotGiven[SemigroupK[F]]): SemigroupK[F] = DerivedSemigroupK[F] + + object monoidK: + inline given [F[_]](using NotGiven[MonoidK[F]]): MonoidK[F] = DerivedMonoidK[F] diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala index bcbf2ec5..7b79c9b3 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -9,24 +9,25 @@ class SemigroupKSuite extends KittensSuite { import TestDefns.* def testSemigroupK(context: String)(implicit - // complexProduct: SemigroupK[ComplexProduct], + complexProduct: SemigroupK[ComplexProduct], caseClassWOption: SemigroupK[CaseClassWOption], boxMul: SemigroupK[BoxMul] ): Unit = { - // checkAll(s"$context.SemigroupK[ComplexProduct]", SemigroupKTests[ComplexProduct].semigroupK[Char]) + checkAll(s"$context.SemigroupK[ComplexProduct]", SemigroupKTests[ComplexProduct].semigroupK[Char]) checkAll(s"$context.SemigroupK[CaseClassWOption]", SemigroupKTests[CaseClassWOption].semigroupK[Char]) checkAll(s"$context.SemigroupK[BoxMul]", SemigroupKTests[BoxMul].semigroupK[Char]) - // checkAll(s"$context.SemigroupK is Serializable", SerializableTests.serializable(SemigroupK[ComplexProduct])) + checkAll(s"$context.SemigroupK is Serializable", SerializableTests.serializable(SemigroupK[ComplexProduct])) test(s"$context.SemigroupK respects existing instances") { assert(boxMul.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } } - // locally { - // import auto.semigroupK.given - // testSemigroupK("auto") - // } + locally { + import auto.semigroupK.given + summon[SemigroupK[[a] =>> Eval[Option[a]]]] + testSemigroupK("auto") + } locally { import semiInstances.given @@ -40,7 +41,7 @@ object SemigroupKSuite { type BoxMul[A] = Box[Mul[A]] object semiInstances { - // implicit val complexProduct: SemigroupK[ComplexProduct] = semiauto.semigroupK + implicit val complexProduct: SemigroupK[ComplexProduct] = semiauto.semigroupK implicit val caseClassWOption: SemigroupK[CaseClassWOption] = semiauto.semigroupK implicit val boxMul: SemigroupK[BoxMul] = semiauto.semigroupK } From fe2b8b44226f4f49212c0bdc2bba26765389fcd5 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Mon, 23 May 2022 11:44:06 +0100 Subject: [PATCH 4/8] Priority for derived SemigroupK given instances --- .../scala-3/cats/derived/DerivedSemigroupK.scala | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala index aa2638b9..26b6f124 100644 --- a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -6,7 +6,7 @@ import shapeless3.deriving.{Const, K1} import scala.compiletime.* type DerivedSemigroupK[F[_]] = Derived[SemigroupK[F]] -object DerivedSemigroupK: +object DerivedSemigroupK extends DerivedSemigroupKInstances1: type Or[F[_]] = Derived.Or[SemigroupK[F]] inline def apply[F[_]]: SemigroupK[F] = import DerivedSemigroupK.given @@ -18,10 +18,6 @@ object DerivedSemigroupK: given [F[_], G[_]](using F: Or[F]): DerivedSemigroupK[[x] =>> F[G[x]]] = F.unify.compose[G] - given [F[_], G[_]](using F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = - new SemigroupK[[x] =>> F[G[x]]]: - final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) - given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = given K1.ProductInstances[SemigroupK, F] = inst.unify new Product[SemigroupK, F] {} @@ -29,3 +25,10 @@ object DerivedSemigroupK: trait Product[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F]) extends SemigroupK[F]: final override def combineK[A](x: F[A], y: F[A]): F[A] = inst.map2[A, A, A](x, y)([t[_]] => (smgrpk: T[t], x: t[A], y: t[A]) => smgrpk.combineK(x, y)) + +trait DerivedSemigroupKInstances1: + import DerivedSemigroupK.Or + + given [F[_], G[_]](using F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = + new SemigroupK[[x] =>> F[G[x]]]: + final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) From 84b06644398f17e1184134fcbc172b208c8bb2a7 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Mon, 23 May 2022 12:01:50 +0100 Subject: [PATCH 5/8] Port scala 2 MonoidK tests to scala 3 --- .../scala-3/cats/derived/DerivedMonoidK.scala | 19 +++++- .../scala-3/cats/derived/MonoidKSuite.scala | 64 +++++++++++++++++++ .../scala-3/cats/derived/MonoidKTests.scala | 9 --- 3 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 core/src/test/scala-3/cats/derived/MonoidKSuite.scala delete mode 100644 core/src/test/scala-3/cats/derived/MonoidKTests.scala diff --git a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala index 9b8925f6..d1ca05c2 100644 --- a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala @@ -1,12 +1,12 @@ package cats.derived -import cats.{Monoid, MonoidK} +import cats.* import shapeless3.deriving.{Const, K1} import scala.compiletime.* type DerivedMonoidK[F[_]] = Derived[MonoidK[F]] -object DerivedMonoidK: +object DerivedMonoidK extends DerivedMonoidKInstances1: type Or[F[_]] = Derived.Or[MonoidK[F]] inline def apply[F[_]]: MonoidK[F] = import DerivedMonoidK.given @@ -17,6 +17,9 @@ object DerivedMonoidK: final override def combineK[A](x: Const[T][A], y: Const[T][A]) = T.combine(x, y) + given [F[_], G[_]](using F: Or[F]): DerivedMonoidK[[x] =>> F[G[x]]] = + F.unify.compose[G] + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedMonoidK[F] = given K1.ProductInstances[MonoidK, F] = inst.unify new Product[MonoidK, F] {} @@ -25,3 +28,15 @@ object DerivedMonoidK: extends MonoidK[F], DerivedSemigroupK.Product[T, F]: final override def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A]) + +trait DerivedMonoidKInstances1: + import DerivedMonoidK.Or + + given [F[_], G[_]](using F0: DerivedApplicative.Or[F], G0: Or[G]): DerivedMonoidK[[x] =>> F[G[x]]] = + new MonoidK[[x] =>> F[G[x]]]: + val F: Applicative[F] = F0.unify + val G: MonoidK[G] = G0.unify + + final override def empty[A]: F[G[A]] = F.pure(G.empty[A]) + + final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.map2(x, y)(G.combineK(_, _)) diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala new file mode 100644 index 00000000..b5fa1b18 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -0,0 +1,64 @@ +package cats.derived + +import alleycats.* +import cats.* +import cats.derived.* +import cats.laws.discipline.{MonoidKTests, SerializableTests} +import org.scalacheck.Arbitrary + +class MonoidKSuite extends KittensSuite { + import MonoidKSuite.* + import TestDefns.* + + def testMonoidK(context: String)(implicit + complexProduct: MonoidK[ComplexProduct], + caseClassWOption: MonoidK[CaseClassWOption], + boxMul: MonoidK[BoxMul] + ): Unit = { + checkAll(s"$context.MonoidK[ComplexProduct]", MonoidKTests[ComplexProduct].monoidK[Char]) + checkAll(s"$context.MonoidK[CaseClassWOption]", MonoidKTests[CaseClassWOption].monoidK[Char]) + checkAll(s"$context.MonoidK[BoxMul]", MonoidKTests[BoxMul].monoidK[Char]) + checkAll(s"$context.MonoidK is Serializable", SerializableTests.serializable(MonoidK[ComplexProduct])) + + test(s"$context.MonoidK respects existing instances") { + assert(boxMul.empty[Char] == Box(Mul[Char](1))) + assert(boxMul.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) + } + } + + { + import auto.monoidK.given + testMonoidK("auto") + } + + { + import semiInstances.given + testMonoidK("semi") + } +} + +object MonoidKSuite { + import TestDefns._ + + type BoxMul[A] = Box[Mul[A]] + + object semiInstances { + implicit val complexProduct: MonoidK[ComplexProduct] = semiauto.monoidK + implicit val caseClassWOption: MonoidK[CaseClassWOption] = semiauto.monoidK + implicit val boxMul: MonoidK[BoxMul] = semiauto.monoidK + } + + final case class Mul[T](value: Int) + object Mul { + + implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) + + implicit def arbitrary[T]: Arbitrary[Mul[T]] = + Arbitrary(Arbitrary.arbitrary[Int].map(apply)) + + implicit val monoidK: MonoidK[Mul] = new MonoidK[Mul] { + def empty[A] = Mul(1) + def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) + } + } +} diff --git a/core/src/test/scala-3/cats/derived/MonoidKTests.scala b/core/src/test/scala-3/cats/derived/MonoidKTests.scala deleted file mode 100644 index e85a531e..00000000 --- a/core/src/test/scala-3/cats/derived/MonoidKTests.scala +++ /dev/null @@ -1,9 +0,0 @@ -package cats.derived - -import alleycats.* -import cats.* -import cats.derived.* - -class MonoidKTests { // - case class Foo[A](i: String, l: List[A]) derives MonoidK -} From a1e9750fcb7bb331f3f98f28d55a542680327162 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Mon, 23 May 2022 17:56:32 +0100 Subject: [PATCH 6/8] Various improvements - ImplicitNotFound error for SemigroupK/MonoidK - Replace given priority via traits with NotGiven --- .../scala-3/cats/derived/DerivedMonoidK.scala | 38 ++++++++++++------- .../cats/derived/DerivedSemigroupK.scala | 24 +++++++----- .../scala-3/cats/derived/MonoidKSuite.scala | 4 +- .../cats/derived/SemigroupKSuite.scala | 1 - 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala index d1ca05c2..3f9d64be 100644 --- a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala @@ -3,10 +3,19 @@ package cats.derived import cats.* import shapeless3.deriving.{Const, K1} +import scala.annotation.implicitNotFound import scala.compiletime.* - +import scala.util.NotGiven + +@implicitNotFound("""Could not derive an instance of MonoidK[F] where F = ${F}. +Make sure that F[_] satisfies one of the following conditions: + * it is a constant type [x] =>> T where T: Monoid + * it is a nested type [x] =>> G[H[x]] where G: MonoidK + * it is a nested type [x] =>> G[H[x]] where G: Applicative and H: MonoidK + * it is a generic case class where all fields have a MonoidK instance +""") type DerivedMonoidK[F[_]] = Derived[MonoidK[F]] -object DerivedMonoidK extends DerivedMonoidKInstances1: +object DerivedMonoidK: type Or[F[_]] = Derived.Or[MonoidK[F]] inline def apply[F[_]]: MonoidK[F] = import DerivedMonoidK.given @@ -20,6 +29,19 @@ object DerivedMonoidK extends DerivedMonoidKInstances1: given [F[_], G[_]](using F: Or[F]): DerivedMonoidK[[x] =>> F[G[x]]] = F.unify.compose[G] + given [F[_], G[_]](using + N: NotGiven[Or[F]], + F0: DerivedApplicative.Or[F], + G0: Or[G] + ): DerivedMonoidK[[x] =>> F[G[x]]] = + new MonoidK[[x] =>> F[G[x]]]: + val F: Applicative[F] = F0.unify + val G: MonoidK[G] = G0.unify + + final override def empty[A]: F[G[A]] = F.pure(G.empty[A]) + + final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.map2(x, y)(G.combineK(_, _)) + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedMonoidK[F] = given K1.ProductInstances[MonoidK, F] = inst.unify new Product[MonoidK, F] {} @@ -28,15 +50,3 @@ object DerivedMonoidK extends DerivedMonoidKInstances1: extends MonoidK[F], DerivedSemigroupK.Product[T, F]: final override def empty[A]: F[A] = inst.construct([t[_]] => (emp: T[t]) => emp.empty[A]) - -trait DerivedMonoidKInstances1: - import DerivedMonoidK.Or - - given [F[_], G[_]](using F0: DerivedApplicative.Or[F], G0: Or[G]): DerivedMonoidK[[x] =>> F[G[x]]] = - new MonoidK[[x] =>> F[G[x]]]: - val F: Applicative[F] = F0.unify - val G: MonoidK[G] = G0.unify - - final override def empty[A]: F[G[A]] = F.pure(G.empty[A]) - - final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.map2(x, y)(G.combineK(_, _)) diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala index 26b6f124..b826cf28 100644 --- a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -3,10 +3,19 @@ package cats.derived import cats.{Semigroup, SemigroupK} import shapeless3.deriving.{Const, K1} +import scala.annotation.implicitNotFound import scala.compiletime.* - +import scala.util.NotGiven + +@implicitNotFound("""Could not derive an instance of SemigroupK[F] where F = ${F}. +Make sure that F[_] satisfies one of the following conditions: + * it is a constant type [x] =>> T where T: Semigroup + * it is a nested type [x] =>> G[H[x]] where G: SemigroupK + * it is a nested type [x] =>> G[H[x]] where G: Apply and H: SemigroupK + * it is a generic case class where all fields have a SemigroupK instance +""") type DerivedSemigroupK[F[_]] = Derived[SemigroupK[F]] -object DerivedSemigroupK extends DerivedSemigroupKInstances1: +object DerivedSemigroupK: type Or[F[_]] = Derived.Or[SemigroupK[F]] inline def apply[F[_]]: SemigroupK[F] = import DerivedSemigroupK.given @@ -18,6 +27,10 @@ object DerivedSemigroupK extends DerivedSemigroupKInstances1: given [F[_], G[_]](using F: Or[F]): DerivedSemigroupK[[x] =>> F[G[x]]] = F.unify.compose[G] + given [F[_], G[_]](using N: NotGiven[Or[F]], F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = + new SemigroupK[[x] =>> F[G[x]]]: + final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) + given [F[_]](using inst: => K1.ProductInstances[Or, F]): DerivedSemigroupK[F] = given K1.ProductInstances[SemigroupK, F] = inst.unify new Product[SemigroupK, F] {} @@ -25,10 +38,3 @@ object DerivedSemigroupK extends DerivedSemigroupKInstances1: trait Product[T[x[_]] <: SemigroupK[x], F[_]](using inst: K1.ProductInstances[T, F]) extends SemigroupK[F]: final override def combineK[A](x: F[A], y: F[A]): F[A] = inst.map2[A, A, A](x, y)([t[_]] => (smgrpk: T[t], x: t[A], y: t[A]) => smgrpk.combineK(x, y)) - -trait DerivedSemigroupKInstances1: - import DerivedSemigroupK.Or - - given [F[_], G[_]](using F: DerivedApply.Or[F], G: Or[G]): DerivedSemigroupK[[x] =>> F[G[x]]] = - new SemigroupK[[x] =>> F[G[x]]]: - final override def combineK[A](x: F[G[A]], y: F[G[A]]): F[G[A]] = F.unify.map2(x, y)(G.unify.combineK(_, _)) diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala index b5fa1b18..f36190c5 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -32,7 +32,7 @@ class MonoidKSuite extends KittensSuite { } { - import semiInstances.given + import monInstances.given testMonoidK("semi") } } @@ -42,7 +42,7 @@ object MonoidKSuite { type BoxMul[A] = Box[Mul[A]] - object semiInstances { + object monInstances { implicit val complexProduct: MonoidK[ComplexProduct] = semiauto.monoidK implicit val caseClassWOption: MonoidK[CaseClassWOption] = semiauto.monoidK implicit val boxMul: MonoidK[BoxMul] = semiauto.monoidK diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala index 7b79c9b3..b914fa70 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -25,7 +25,6 @@ class SemigroupKSuite extends KittensSuite { locally { import auto.semigroupK.given - summon[SemigroupK[[a] =>> Eval[Option[a]]]] testSemigroupK("auto") } From 819ab4f8bdc3ecde470ef63002472c3dce0d171f Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 11:00:26 +0100 Subject: [PATCH 7/8] Use inline in tests --- .../scala-3/cats/derived/DerivedMonoidK.scala | 3 +-- .../cats/derived/DerivedSemigroupK.scala | 3 +-- .../scala-3/cats/derived/MonoidKSuite.scala | 22 +++++++++---------- .../cats/derived/SemigroupKSuite.scala | 22 ++++++++++--------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala index 3f9d64be..69cc75d0 100644 --- a/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedMonoidK.scala @@ -12,8 +12,7 @@ Make sure that F[_] satisfies one of the following conditions: * it is a constant type [x] =>> T where T: Monoid * it is a nested type [x] =>> G[H[x]] where G: MonoidK * it is a nested type [x] =>> G[H[x]] where G: Applicative and H: MonoidK - * it is a generic case class where all fields have a MonoidK instance -""") + * it is a generic case class where all fields have a MonoidK instance""") type DerivedMonoidK[F[_]] = Derived[MonoidK[F]] object DerivedMonoidK: type Or[F[_]] = Derived.Or[MonoidK[F]] diff --git a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala index b826cf28..d3099cfb 100644 --- a/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala +++ b/core/src/main/scala-3/cats/derived/DerivedSemigroupK.scala @@ -12,8 +12,7 @@ Make sure that F[_] satisfies one of the following conditions: * it is a constant type [x] =>> T where T: Semigroup * it is a nested type [x] =>> G[H[x]] where G: SemigroupK * it is a nested type [x] =>> G[H[x]] where G: Apply and H: SemigroupK - * it is a generic case class where all fields have a SemigroupK instance -""") + * it is a generic case class where all fields have a SemigroupK instance""") type DerivedSemigroupK[F[_]] = Derived[SemigroupK[F]] object DerivedSemigroupK: type Or[F[_]] = Derived.Or[SemigroupK[F]] diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala index f36190c5..eedc7e28 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -5,24 +5,24 @@ import cats.* import cats.derived.* import cats.laws.discipline.{MonoidKTests, SerializableTests} import org.scalacheck.Arbitrary +import scala.compiletime.* class MonoidKSuite extends KittensSuite { import MonoidKSuite.* import TestDefns.* - def testMonoidK(context: String)(implicit - complexProduct: MonoidK[ComplexProduct], - caseClassWOption: MonoidK[CaseClassWOption], - boxMul: MonoidK[BoxMul] - ): Unit = { - checkAll(s"$context.MonoidK[ComplexProduct]", MonoidKTests[ComplexProduct].monoidK[Char]) - checkAll(s"$context.MonoidK[CaseClassWOption]", MonoidKTests[CaseClassWOption].monoidK[Char]) - checkAll(s"$context.MonoidK[BoxMul]", MonoidKTests[BoxMul].monoidK[Char]) - checkAll(s"$context.MonoidK is Serializable", SerializableTests.serializable(MonoidK[ComplexProduct])) + inline def monoidKTests[F[_]]: MonoidKTests[F] = MonoidKTests[F](summonInline) + + inline def testMonoidK(context: String): Unit = { + checkAll(s"$context.MonoidK[ComplexProduct]", monoidKTests[ComplexProduct].monoidK[Char]) + checkAll(s"$context.MonoidK[CaseClassWOption]", monoidKTests[CaseClassWOption].monoidK[Char]) + checkAll(s"$context.MonoidK[BoxMul]", monoidKTests[BoxMul].monoidK[Char]) + checkAll(s"$context.MonoidK is Serializable", SerializableTests.serializable(summonInline[MonoidK[ComplexProduct]])) test(s"$context.MonoidK respects existing instances") { - assert(boxMul.empty[Char] == Box(Mul[Char](1))) - assert(boxMul.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) + val M = summonInline[MonoidK[BoxMul]] + assert(M.empty[Char] == Box(Mul[Char](1))) + assert(M.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } } diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala index b914fa70..b7eaa75b 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -3,23 +3,25 @@ package cats.derived import cats.* import cats.laws.discipline.{SemigroupKTests, SerializableTests} import org.scalacheck.Arbitrary +import scala.compiletime.* class SemigroupKSuite extends KittensSuite { import SemigroupKSuite.* import TestDefns.* - def testSemigroupK(context: String)(implicit - complexProduct: SemigroupK[ComplexProduct], - caseClassWOption: SemigroupK[CaseClassWOption], - boxMul: SemigroupK[BoxMul] - ): Unit = { - checkAll(s"$context.SemigroupK[ComplexProduct]", SemigroupKTests[ComplexProduct].semigroupK[Char]) - checkAll(s"$context.SemigroupK[CaseClassWOption]", SemigroupKTests[CaseClassWOption].semigroupK[Char]) - checkAll(s"$context.SemigroupK[BoxMul]", SemigroupKTests[BoxMul].semigroupK[Char]) - checkAll(s"$context.SemigroupK is Serializable", SerializableTests.serializable(SemigroupK[ComplexProduct])) + inline def semigroupKTests[F[_]]: SemigroupKTests[F] = SemigroupKTests[F](summonInline) + + inline def testSemigroupK(context: String): Unit = { + checkAll(s"$context.SemigroupK[ComplexProduct]", semigroupKTests[ComplexProduct].semigroupK[Char]) + checkAll(s"$context.SemigroupK[CaseClassWOption]", semigroupKTests[CaseClassWOption].semigroupK[Char]) + checkAll(s"$context.SemigroupK[BoxMul]", semigroupKTests[BoxMul].semigroupK[Char]) + checkAll( + s"$context.SemigroupK is Serializable", + SerializableTests.serializable(summonInline[SemigroupK[ComplexProduct]]) + ) test(s"$context.SemigroupK respects existing instances") { - assert(boxMul.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) + assert(summonInline[SemigroupK[BoxMul]].combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } } From fc2fced5ac1cc0d66a009132e6bdef0f580822f4 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 11:15:46 +0100 Subject: [PATCH 8/8] SemigroupK/MonoidK test for derives syntax --- core/src/test/scala-3/cats/derived/MonoidKSuite.scala | 3 +++ core/src/test/scala-3/cats/derived/SemigroupKSuite.scala | 3 +++ 2 files changed, 6 insertions(+) diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala index eedc7e28..9a0ad5ad 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -61,4 +61,7 @@ object MonoidKSuite { def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) } } + + case class Simple[A](value1: List[A], value2: Set[A]) derives MonoidK + case class Recursive[A](first: List[A], rest: Recursive[A]) derives MonoidK } diff --git a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala index b7eaa75b..6214ac03 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -59,4 +59,7 @@ object SemigroupKSuite { def combineK[A](x: Mul[A], y: Mul[A]) = Mul(x.value * y.value) } } + + case class Simple[A](value1: List[A], value2: Set[A]) derives SemigroupK + case class Recursive[A](first: List[A], rest: Recursive[A]) derives SemigroupK }