From 6cca3981113fdd1d9796fab0f8d8fd7d9b3d13d3 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Fri, 20 May 2022 16:40:58 +0100 Subject: [PATCH 01/14] 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 02/14] 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 03/14] 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 04/14] 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 05/14] 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 06/14] 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 07/14] 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 08/14] 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 } From d241800183e72a38ac3879707541a628d499344e Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 11:48:22 +0100 Subject: [PATCH 09/14] Port Contravariant to new derivation scheme --- .../cats/derived/DerivedContravariant.scala | 35 +++++++++++++++++++ .../scala-3/cats/derived/contravariant.scala | 15 -------- .../main/scala-3/cats/derived/package.scala | 7 +++- .../cats/derived/ContravariantTests.scala | 2 +- 4 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/DerivedContravariant.scala delete mode 100644 core/src/main/scala-3/cats/derived/contravariant.scala diff --git a/core/src/main/scala-3/cats/derived/DerivedContravariant.scala b/core/src/main/scala-3/cats/derived/DerivedContravariant.scala new file mode 100644 index 00000000..b3cec8e8 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/DerivedContravariant.scala @@ -0,0 +1,35 @@ +package cats.derived + +import cats.{Contravariant, Functor} +import shapeless3.deriving.{Const, K1} + +import scala.annotation.implicitNotFound +import scala.compiletime.* + +@implicitNotFound("""Could not derive an instance of Contravariant[F] where F = ${F}. +Make sure that F[_] satisfies one of the following conditions: + * it is a constant type [x] =>> T + * it is a nested type [x] =>> G[H[x]] where G: Functor and H: Contravariant + * it is a generic case class where all fields have a Contravariant instance + * it is a generic sealed trait where all subclasses have a Contravariant instance""") +type DerivedContravariant[F[_]] = Derived[Contravariant[F]] +object DerivedContravariant: + type Or[F[_]] = Derived.Or[Contravariant[F]] + inline def apply[F[_]]: Contravariant[F] = + import DerivedContravariant.given + summonInline[DerivedContravariant[F]].instance + + given [T]: DerivedContravariant[Const[T]] = new Contravariant[Const[T]]: + def contramap[A, B](fa: T)(f: B => A): T = fa + + given [F[_], G[_]](using F: DerivedFunctor.Or[F], G: Or[G]): DerivedContravariant[[x] =>> F[G[x]]] = + given Contravariant[G] = G.unify + F.unify.composeContravariant[G] + + given [F[_]](using inst: => K1.Instances[Or, F]): DerivedContravariant[F] = + given K1.Instances[Contravariant, F] = inst.unify + new Generic[Contravariant, F] {} + + trait Generic[T[x[_]] <: Contravariant[x], F[_]](using inst: K1.Instances[T, F]) extends Contravariant[F]: + final override def contramap[A, B](fa: F[A])(f: B => A): F[B] = + inst.map(fa: F[A])([f[_]] => (tf: T[f], fa: f[A]) => tf.contramap(fa)(f)) diff --git a/core/src/main/scala-3/cats/derived/contravariant.scala b/core/src/main/scala-3/cats/derived/contravariant.scala deleted file mode 100644 index e55af0c3..00000000 --- a/core/src/main/scala-3/cats/derived/contravariant.scala +++ /dev/null @@ -1,15 +0,0 @@ -package cats.derived - -import cats.Contravariant -import shapeless3.deriving.K1 - -trait GenericContravariant[T[x[_]] <: Contravariant[x], F[_]](using inst: K1.Instances[T, F]) extends Contravariant[F]: - - def contramap[A, B](fa: F[A])(f: B => A): F[B] = inst.map(fa)( - [t[_]] => (contra: T[t], t0: t[A]) => contra.contramap(t0)(f) - ) - -trait ContravariantDerivation: - extension (F: Contravariant.type) - inline def derived[F[_]](using gen: K1.Generic[F]): Contravariant[F] = - new GenericContravariant[Contravariant, F] {} diff --git a/core/src/main/scala-3/cats/derived/package.scala b/core/src/main/scala-3/cats/derived/package.scala index 2fa7c556..dd4fcee4 100644 --- a/core/src/main/scala-3/cats/derived/package.scala +++ b/core/src/main/scala-3/cats/derived/package.scala @@ -26,8 +26,9 @@ extension (x: Traverse.type) inline def derived[F[_]]: Traverse[F] = DerivedTrav 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] +extension (x: Contravariant.type) inline def derived[F[_]]: Contravariant[F] = DerivedContravariant[F] -object semiauto extends ContravariantDerivation, InvariantDerivation, PartialOrderDerivation, Instances: +object semiauto extends InvariantDerivation, PartialOrderDerivation, Instances: inline def eq[A]: Eq[A] = DerivedEq[A] inline def hash[A]: Hash[A] = DerivedHash[A] @@ -49,6 +50,7 @@ object semiauto extends ContravariantDerivation, InvariantDerivation, PartialOrd inline def show[A]: Show[A] = DerivedShow[A] inline def semigroupK[F[_]]: SemigroupK[F] = DerivedSemigroupK[F] inline def monoidK[F[_]]: MonoidK[F] = DerivedMonoidK[F] + inline def contravariant[F[_]]: Contravariant[F] = DerivedContravariant[F] object auto: object eq: @@ -110,3 +112,6 @@ object auto: object monoidK: inline given [F[_]](using NotGiven[MonoidK[F]]): MonoidK[F] = DerivedMonoidK[F] + + object contravariant: + inline given [F[_]](using NotGiven[Contravariant[F]]): Contravariant[F] = DerivedContravariant[F] diff --git a/core/src/test/scala-3/cats/derived/ContravariantTests.scala b/core/src/test/scala-3/cats/derived/ContravariantTests.scala index 47955456..dbb6262a 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantTests.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantTests.scala @@ -1,7 +1,7 @@ package cats.derived import cats.Contravariant -import cats.derived.semiauto.* +import cats.derived.given class ContravariantTests { From b4aa026a24cc85a50edbbf158f9189d0ff0df4b4 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 12:08:09 +0100 Subject: [PATCH 10/14] WIP port scala 2 Contravariant tests to scala 3 --- .../cats/derived/ContravariantSuite.scala | 90 +++++++++++++++++++ .../cats/derived/ContravariantTests.scala | 10 --- 2 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 core/src/test/scala-3/cats/derived/ContravariantSuite.scala delete mode 100644 core/src/test/scala-3/cats/derived/ContravariantTests.scala diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala new file mode 100644 index 00000000..b0e99704 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015 Miles Sabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cats +package derived + +import cats.laws.discipline._ +import cats.laws.discipline.arbitrary._ +import cats.laws.discipline.eq._ +import scala.compiletime.* + +class ContravariantSuite extends KittensSuite { + import ContravariantSuite.* + import TestDefns.* + + inline def contravariantTests[F[_]]: ContravariantTests[F] = ContravariantTests[F](summonInline) + + inline def testContravariant(context: String): Unit = { + checkAll(s"$context.Contravariant[OptPred]", contravariantTests[OptPred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$context.Contravariant[TreePred]", contravariantTests[TreePred].contravariant[MiniInt, String, Boolean]) + checkAll(s"$context.Contravariant[ListPred]", contravariantTests[ListPred].contravariant[MiniInt, String, Boolean]) + checkAll( + s"$context.Contravariant[GenericAdtPred]", + contravariantTests[GenericAdtPred].contravariant[MiniInt, String, Boolean] + ) + // checkAll( + // s"$context.Contravariant[InterleavedPred]", + // ContravariantTests[InterleavedPred].contravariant[MiniInt, String, Boolean] + // ) + checkAll( + s"$context.Contravariant[AndCharPred]", + contravariantTests[AndCharPred].contravariant[MiniInt, String, Boolean] + ) + checkAll(s"$context.Contravariant is Serializable", SerializableTests.serializable(summonInline[Contravariant[TreePred]])) + + // test(s"$context.Contravariant.contramap is stack safe") { + // val C = summonInline[Contravariant[ListSnocF]] + // val n = 10000 + // val largeBoxed = Snoc.fromSeq((1 until n).map((j: Int) => (i: Int) => i + j)) :: Nil + // val actualBoxed = C.contramap[Int, Int](largeBoxed)((j: Int) => j + 1).flatMap(Snoc.toList) + // val expected = (3 until n + 2).toList + // assert(actualBoxed.map(_.apply(1)) == expected) + // } + } + + { + import auto.contravariant.given + testContravariant("auto") + } + + { + import semiInstances.given + testContravariant("semiauto") + } +} + +object ContravariantSuite { + import TestDefns._ + + type OptPred[A] = Option[A => Boolean] + type ListPred[A] = List[A => Boolean] + type GenericAdtPred[A] = GenericAdt[A => Boolean] + type ListSnocF[A] = List[Snoc[A => Int]] + type InterleavedPred[A] = Interleaved[A => Boolean] + type AndCharPred[A] = (A => Boolean, Char) + type TreePred[A] = Tree[A => Boolean] + + object semiInstances { + implicit val optPred: Contravariant[OptPred] = semiauto.contravariant + implicit val treePred: Contravariant[TreePred] = semiauto.contravariant + implicit val listPred: Contravariant[ListPred] = semiauto.contravariant + implicit val genericAdtPred: Contravariant[GenericAdtPred] = semiauto.contravariant + // implicit val interleavePred: Contravariant[InterleavedPred] = semiauto.contravariant + implicit val andCharPred: Contravariant[AndCharPred] = semiauto.contravariant + // implicit val listSnocF: Contravariant[ListSnocF] = semiauto.contravariant + } +} diff --git a/core/src/test/scala-3/cats/derived/ContravariantTests.scala b/core/src/test/scala-3/cats/derived/ContravariantTests.scala deleted file mode 100644 index dbb6262a..00000000 --- a/core/src/test/scala-3/cats/derived/ContravariantTests.scala +++ /dev/null @@ -1,10 +0,0 @@ -package cats.derived - -import cats.Contravariant -import cats.derived.given - -class ContravariantTests { - - case class Foo[A](f: A => String) derives Contravariant - -} From 813704b93f8efa475313a97f53d9ea5424b3f836 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 13:00:59 +0100 Subject: [PATCH 11/14] Link commented out tests to issues --- .../scala-3/cats/derived/ContravariantSuite.scala | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala index b0e99704..9bb9a9c6 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -36,6 +36,7 @@ class ContravariantSuite extends KittensSuite { s"$context.Contravariant[GenericAdtPred]", contravariantTests[GenericAdtPred].contravariant[MiniInt, String, Boolean] ) + // TODO https://github.com/typelevel/kittens/issues/473 // checkAll( // s"$context.Contravariant[InterleavedPred]", // ContravariantTests[InterleavedPred].contravariant[MiniInt, String, Boolean] @@ -44,8 +45,16 @@ class ContravariantSuite extends KittensSuite { s"$context.Contravariant[AndCharPred]", contravariantTests[AndCharPred].contravariant[MiniInt, String, Boolean] ) - checkAll(s"$context.Contravariant is Serializable", SerializableTests.serializable(summonInline[Contravariant[TreePred]])) + checkAll( + s"$context.Contravariant[ListSnocF]", + contravariantTests[ListSnocF].contravariant[MiniInt, String, Boolean] + ) + checkAll( + s"$context.Contravariant is Serializable", + SerializableTests.serializable(summonInline[Contravariant[TreePred]]) + ) + // TODO https://github.com/typelevel/kittens/issues/476 // test(s"$context.Contravariant.contramap is stack safe") { // val C = summonInline[Contravariant[ListSnocF]] // val n = 10000 @@ -85,6 +94,6 @@ object ContravariantSuite { implicit val genericAdtPred: Contravariant[GenericAdtPred] = semiauto.contravariant // implicit val interleavePred: Contravariant[InterleavedPred] = semiauto.contravariant implicit val andCharPred: Contravariant[AndCharPred] = semiauto.contravariant - // implicit val listSnocF: Contravariant[ListSnocF] = semiauto.contravariant + implicit val listSnocF: Contravariant[ListSnocF] = semiauto.contravariant } } From d928cd527a169b3f4306737346c6316dfe794925 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Tue, 24 May 2022 14:11:11 +0100 Subject: [PATCH 12/14] derives syntax tests for Contravariant --- .../cats/derived/ContravariantSuite.scala | 30 ++++++++++++------- .../scala-3/cats/derived/MonoidKSuite.scala | 19 +++++------- .../cats/derived/SemigroupKSuite.scala | 17 ++++------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala index 9bb9a9c6..09369d25 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -22,13 +22,13 @@ import cats.laws.discipline.arbitrary._ import cats.laws.discipline.eq._ import scala.compiletime.* -class ContravariantSuite extends KittensSuite { +class ContravariantSuite extends KittensSuite: import ContravariantSuite.* import TestDefns.* inline def contravariantTests[F[_]]: ContravariantTests[F] = ContravariantTests[F](summonInline) - inline def testContravariant(context: String): Unit = { + inline def testContravariant(context: String): Unit = checkAll(s"$context.Contravariant[OptPred]", contravariantTests[OptPred].contravariant[MiniInt, String, Boolean]) checkAll(s"$context.Contravariant[TreePred]", contravariantTests[TreePred].contravariant[MiniInt, String, Boolean]) checkAll(s"$context.Contravariant[ListPred]", contravariantTests[ListPred].contravariant[MiniInt, String, Boolean]) @@ -63,20 +63,18 @@ class ContravariantSuite extends KittensSuite { // val expected = (3 until n + 2).toList // assert(actualBoxed.map(_.apply(1)) == expected) // } - } - { + locally { import auto.contravariant.given testContravariant("auto") } - { + locally { import semiInstances.given testContravariant("semiauto") } -} -object ContravariantSuite { +object ContravariantSuite: import TestDefns._ type OptPred[A] = Option[A => Boolean] @@ -87,7 +85,7 @@ object ContravariantSuite { type AndCharPred[A] = (A => Boolean, Char) type TreePred[A] = Tree[A => Boolean] - object semiInstances { + object semiInstances: implicit val optPred: Contravariant[OptPred] = semiauto.contravariant implicit val treePred: Contravariant[TreePred] = semiauto.contravariant implicit val listPred: Contravariant[ListPred] = semiauto.contravariant @@ -95,5 +93,17 @@ object ContravariantSuite { // implicit val interleavePred: Contravariant[InterleavedPred] = semiauto.contravariant implicit val andCharPred: Contravariant[AndCharPred] = semiauto.contravariant implicit val listSnocF: Contravariant[ListSnocF] = semiauto.contravariant - } -} + + case class Single[A](value: A) derives Functor + + enum Many[+A] derives Functor: + case Naught + case More(value: A, rest: Many[A]) + + enum AtMostOne[+A] derives Functor: + case Naught + case Single(value: A) + + enum AtLeastOne[+A] derives Functor: + case Single(value: A) + case More(value: A, rest: Option[AtLeastOne[A]]) diff --git a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala index 9a0ad5ad..8a605417 100644 --- a/core/src/test/scala-3/cats/derived/MonoidKSuite.scala +++ b/core/src/test/scala-3/cats/derived/MonoidKSuite.scala @@ -7,13 +7,13 @@ import cats.laws.discipline.{MonoidKTests, SerializableTests} import org.scalacheck.Arbitrary import scala.compiletime.* -class MonoidKSuite extends KittensSuite { +class MonoidKSuite extends KittensSuite: import MonoidKSuite.* import TestDefns.* inline def monoidKTests[F[_]]: MonoidKTests[F] = MonoidKTests[F](summonInline) - inline def testMonoidK(context: String): Unit = { + 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]) @@ -24,32 +24,29 @@ class MonoidKSuite extends KittensSuite { assert(M.empty[Char] == Box(Mul[Char](1))) assert(M.combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } - } - { + locally { import auto.monoidK.given testMonoidK("auto") } - { + locally { import monInstances.given testMonoidK("semi") } -} -object MonoidKSuite { +object MonoidKSuite: import TestDefns._ type BoxMul[A] = Box[Mul[A]] - object monInstances { + object monInstances: 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 { + object Mul: implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) @@ -60,8 +57,6 @@ object MonoidKSuite { def empty[A] = Mul(1) 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 6214ac03..179e7058 100644 --- a/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala +++ b/core/src/test/scala-3/cats/derived/SemigroupKSuite.scala @@ -5,13 +5,13 @@ import cats.laws.discipline.{SemigroupKTests, SerializableTests} import org.scalacheck.Arbitrary import scala.compiletime.* -class SemigroupKSuite extends KittensSuite { +class SemigroupKSuite extends KittensSuite: import SemigroupKSuite.* import TestDefns.* inline def semigroupKTests[F[_]]: SemigroupKTests[F] = SemigroupKTests[F](summonInline) - inline def testSemigroupK(context: String): Unit = { + 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]) @@ -23,7 +23,6 @@ class SemigroupKSuite extends KittensSuite { test(s"$context.SemigroupK respects existing instances") { assert(summonInline[SemigroupK[BoxMul]].combineK(Box(Mul[Char](5)), Box(Mul[Char](5))) == Box(Mul[Char](25))) } - } locally { import auto.semigroupK.given @@ -34,21 +33,19 @@ class SemigroupKSuite extends KittensSuite { import semiInstances.given testSemigroupK("semiauto") } -} -object SemigroupKSuite { - import TestDefns._ +object SemigroupKSuite: + import TestDefns.* type BoxMul[A] = Box[Mul[A]] - object semiInstances { + 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 { + object Mul: implicit def eqv[T]: Eq[Mul[T]] = Eq.by(_.value) @@ -58,8 +55,6 @@ object SemigroupKSuite { implicit val semigroupK: SemigroupK[Mul] = new SemigroupK[Mul] { 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 -} From 73d8dfaba37082ee52e0ae8d1a6946f3e608fd52 Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 25 May 2022 11:42:59 +0100 Subject: [PATCH 13/14] derives syntax tests for Contravariant --- .../cats/derived/ContravariantSuite.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala index 09369d25..c86d766f 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -94,16 +94,16 @@ object ContravariantSuite: implicit val andCharPred: Contravariant[AndCharPred] = semiauto.contravariant implicit val listSnocF: Contravariant[ListSnocF] = semiauto.contravariant - case class Single[A](value: A) derives Functor + case class Single[A](value: A => Unit) derives Contravariant - enum Many[+A] derives Functor: + enum Many[-A] derives Contravariant: case Naught - case More(value: A, rest: Many[A]) + case More(value: A => Unit, rest: Many[A]) - enum AtMostOne[+A] derives Functor: + enum AtMostOne[-A] derives Contravariant: case Naught - case Single(value: A) + case Single(value: A => Unit) - enum AtLeastOne[+A] derives Functor: - case Single(value: A) - case More(value: A, rest: Option[AtLeastOne[A]]) + enum AtLeastOne[-A] derives Contravariant: + case Single(value: A => Unit) + case More(value: A => Unit, rest: Option[AtLeastOne[A]]) From fd5423b2c475d277d492e6701c056aec4a5be28c Mon Sep 17 00:00:00 2001 From: Tim Spence Date: Wed, 25 May 2022 11:49:47 +0100 Subject: [PATCH 14/14] Scala 3 import syntax --- .../test/scala-3/cats/derived/ContravariantSuite.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala index c86d766f..0817f2ea 100644 --- a/core/src/test/scala-3/cats/derived/ContravariantSuite.scala +++ b/core/src/test/scala-3/cats/derived/ContravariantSuite.scala @@ -17,9 +17,9 @@ package cats package derived -import cats.laws.discipline._ -import cats.laws.discipline.arbitrary._ -import cats.laws.discipline.eq._ +import cats.laws.discipline.* +import cats.laws.discipline.arbitrary.* +import cats.laws.discipline.eq.* import scala.compiletime.* class ContravariantSuite extends KittensSuite: @@ -75,7 +75,7 @@ class ContravariantSuite extends KittensSuite: } object ContravariantSuite: - import TestDefns._ + import TestDefns.* type OptPred[A] = Option[A => Boolean] type ListPred[A] = List[A => Boolean]