From 840ef513e52b07d35118347d8ae7bc494182b631 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Fri, 21 May 2021 20:32:28 +0300 Subject: [PATCH 1/6] [WIP] Reducible --- core/src/main/scala-3/cats/derived/all.scala | 1 + .../main/scala-3/cats/derived/foldable.scala | 14 ++--- .../main/scala-3/cats/derived/reducible.scala | 58 +++++++++++++++++++ .../scala-3/cats/derived/ReducibleTests.scala | 23 ++++++++ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/reducible.scala create mode 100644 core/src/test/scala-3/cats/derived/ReducibleTests.scala diff --git a/core/src/main/scala-3/cats/derived/all.scala b/core/src/main/scala-3/cats/derived/all.scala index dc7f2b14..53786c86 100644 --- a/core/src/main/scala-3/cats/derived/all.scala +++ b/core/src/main/scala-3/cats/derived/all.scala @@ -13,6 +13,7 @@ object all extends MonoidKDerivation, OrderDerivation, PartialOrderDerivation, + ReducibleDerivation, SemigroupDerivation, SemigroupKDerivation, ShowDerivation, diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 48539f70..02675272 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -18,6 +18,9 @@ trait ProductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstan [t[_]] => (fd: T[t], t0: t[A], acc: Eval[B]) => Continue(fd.foldRight(t0, acc)(f)) ) +object ProductFoldable: + given instance[F[_]](using K1.ProductInstances[Foldable, F]): ProductFoldable[Foldable, F] with {} + trait CoproductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) extends Foldable[F]: @@ -31,16 +34,13 @@ trait CoproductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductIn [t[_]] => (fd: T[t], t0: t[A]) => fd.foldRight(t0, lb)(f) ) +object CoproductFoldable: + given instance[F[_]](using K1.CoproductInstances[Foldable, F]): CoproductFoldable[Foldable, F] with {} + trait FoldableDerivation: extension (F: Foldable.type) inline def derived[F[_]](using gen: K1.Generic[F]): Foldable[F] = - gen.derive(productFoldable[F], coproductFoldable[F]) - - given productFoldable[F[_]](using inst: => K1.ProductInstances[Foldable, F]): Foldable[F] = - new ProductFoldable[Foldable, F]{} - - given coproductFoldable[F[_]](using inst: => K1.CoproductInstances[Foldable, F]): Foldable[F] = - new CoproductFoldable[Foldable, F]{} + gen.derive(ProductFoldable.instance, CoproductFoldable.instance) given [X]: Foldable[Const[X]] with def foldLeft[A, B](fa: X, b: B)(f: (B, A) => B): B = b diff --git a/core/src/main/scala-3/cats/derived/reducible.scala b/core/src/main/scala-3/cats/derived/reducible.scala new file mode 100644 index 00000000..4eb32d2b --- /dev/null +++ b/core/src/main/scala-3/cats/derived/reducible.scala @@ -0,0 +1,58 @@ +package cats.derived + +import cats.{Eval, Foldable, Reducible} +import shapeless3.deriving.{Continue, K1} + +object reducible extends ReducibleDerivation + +trait ProductReducible[T[x[_]] <: Foldable[x], F[_]](ev: Any)(using inst: K1.ProductInstances[T, F]) + extends ProductFoldable[T, F], Reducible[F]: + + private val none = Eval.now(None) + + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = + inst.foldLeft[A, Option[B]](fa)(None)( + [t[_]] => (acc: Option[B], fd: T[t], t0: t[A]) => + acc match + case Some(b) => Continue(Some(fd.foldLeft(t0, b)(g))) + case None => Continue(fd.reduceLeftToOption(t0)(f)(g)) + ).get + + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.foldRight[A, Eval[Option[B]]](fa)(none)( + [t[_]] => (fd: T[t], t0: t[A], acc: Eval[Option[B]]) => + Continue(acc.flatMap { + case Some(b) => fd.foldRight(t0, Eval.now(b))(g).map(Some.apply) + case None => fd.reduceRightToOption(t0)(f)(g) + }) + ).map(_.get) + +object ProductReducible: + def instance[F[_]](ev: Any)(using gen: K1.ProductInstances[Foldable, F]): ProductReducible[Foldable, F] = + new ProductReducible[Foldable, F](ev){} + +trait CoproductReducible[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F]) + extends CoproductFoldable[T, F], Reducible[F]: + + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = + inst.fold[A, B](fa)( + [t[_]] => (fd: T[t], t0: t[A]) => fd.reduceLeftTo(t0)(f)(g) + ) + + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.fold[A, Eval[B]](fa)( + [t[_]] => (fd: T[t], t0: t[A]) => fd.reduceRightTo(t0)(f)(g) + ) + +object CoproductReducible: + given instance[F[_]](using K1.CoproductInstances[Reducible, F]): CoproductReducible[Reducible, F] with {} + +trait ReducibleDerivation: + extension (F: Reducible.type) + inline def derived[F[_]](using gen: K1.Generic[F]): Reducible[F] = + inline gen match + case given K1.ProductGeneric[F] => + val ev = K1.summonFirst0[K1.LiftP[Reducible, gen.MirroredElemTypes]] + ProductReducible.instance(ev) + case given K1.CoproductGeneric[F] => CoproductReducible.instance + diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala new file mode 100644 index 00000000..53261f61 --- /dev/null +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -0,0 +1,23 @@ +package cats.derived + +import cats.Reducible +import cats.data.NonEmptyList +import cats.derived.all._ +import cats.derived.all.given + +class ReducibleTests { + + case class Box[A](value: A) derives Reducible + + sealed trait OneOrMany[+A] + case class One[+A](value: A) extends OneOrMany[A] + case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] + + Reducible.derived[One] + Reducible.derived[Many] + Reducible.derived[OneOrMany] + + sealed trait CList[A] + case object CNil extends CList[Nothing] + case class CCons[A](head: A, tail: CCons[A]) extends CList[A] +} From 43c6019bfcb2108393cc7f1a1ea280de3d303930 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Wed, 26 May 2021 13:34:38 +0300 Subject: [PATCH 2/6] Use OrElse to derive Reducible --- .../main/scala-3/cats/derived/foldable.scala | 63 +++++++------- .../main/scala-3/cats/derived/functor.scala | 28 +++--- .../main/scala-3/cats/derived/orelse.scala | 21 +++++ .../main/scala-3/cats/derived/reducible.scala | 85 ++++++++++--------- .../main/scala-3/cats/derived/traverse.scala | 73 +++++++++------- .../scala-3/cats/derived/FoldableTests.scala | 4 +- .../scala-3/cats/derived/FunctorTests.scala | 2 +- .../scala-3/cats/derived/ReducibleTests.scala | 19 +++-- .../scala-3/cats/derived/TraverseTests.scala | 2 +- 9 files changed, 174 insertions(+), 123 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/orelse.scala diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 02675272..03c5c1da 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -5,43 +5,46 @@ import shapeless3.deriving.{Const, Continue, K1} object foldable extends FoldableDerivation -trait ProductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F]) - extends Foldable[F]: +trait DerivedFoldable[F[_]] extends Foldable[F] +object DerivedFoldable: + type Of[F[_]] = Foldable[F] OrElse DerivedFoldable[F] - def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = - inst.foldLeft[A, B](fa)(b)( - [t[_]] => (acc: B, fd: T[t], t0: t[A]) => Continue(fd.foldLeft(t0, acc)(f)) - ) + given [T]: DerivedFoldable[Const[T]] with + def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b + def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb - def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - inst.foldRight[A, Eval[B]](fa)(lb)( - [t[_]] => (fd: T[t], t0: t[A], acc: Eval[B]) => Continue(fd.foldRight(t0, acc)(f)) - ) + def product[F[_]](using inst: => K1.ProductInstances[Of, F]): DerivedFoldable[F] = new Product[Of, F] {} + def coproduct[F[_]](using inst: => K1.CoproductInstances[Of, F]): DerivedFoldable[F] = new Coproduct[Of, F] {} -object ProductFoldable: - given instance[F[_]](using K1.ProductInstances[Foldable, F]): ProductFoldable[Foldable, F] with {} + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedFoldable[F] = + gen.derive(product, coproduct) -trait CoproductFoldable[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends Foldable[F]: + trait Product[T[x[_]] <: Of[x], F[_]](using inst: K1.ProductInstances[T, F]) + extends DerivedFoldable[F]: - def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = - inst.fold[A, B](fa)( - [t[_]] => (fd: T[t], t0: t[A]) => fd.foldLeft(t0, b)(f) - ) + def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = + inst.foldLeft[A, B](fa)(b) { [f[_]] => (acc: B, tf: T[f], fa: f[A]) => + Continue(tf.unify.foldLeft(fa, acc)(f)) + } - def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = - inst.fold[A, Eval[B]](fa)( - [t[_]] => (fd: T[t], t0: t[A]) => fd.foldRight(t0, lb)(f) - ) + def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.foldRight[A, Eval[B]](fa)(lb) { [f[_]] => (tf: T[f], fa: f[A], acc: Eval[B]) => + Continue(Eval.defer(tf.unify.foldRight(fa, acc)(f))) + } -object CoproductFoldable: - given instance[F[_]](using K1.CoproductInstances[Foldable, F]): CoproductFoldable[Foldable, F] with {} + trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + extends DerivedFoldable[F]: + + def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = + inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) => + tf.unify.foldLeft(fa, b)(f) + } + + def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) => + Eval.defer(tf.unify.foldRight(fa, lb)(f)) + } trait FoldableDerivation: extension (F: Foldable.type) - inline def derived[F[_]](using gen: K1.Generic[F]): Foldable[F] = - gen.derive(ProductFoldable.instance, CoproductFoldable.instance) - - given [X]: Foldable[Const[X]] with - def foldLeft[A, B](fa: X, b: B)(f: (B, A) => B): B = b - def foldRight[A, B](fa: X, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb + def derived[F[_]](using instance: => DerivedFoldable[F]): Foldable[F] = instance diff --git a/core/src/main/scala-3/cats/derived/functor.scala b/core/src/main/scala-3/cats/derived/functor.scala index bf24ae6b..dd5511cb 100644 --- a/core/src/main/scala-3/cats/derived/functor.scala +++ b/core/src/main/scala-3/cats/derived/functor.scala @@ -5,18 +5,24 @@ import shapeless3.deriving.{Const, K1} object functor extends FunctorDerivation -trait GenericFunctor[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F]) - extends Functor[F]: +trait DerivedFunctor[F[_]] extends Functor[F] +object DerivedFunctor: + type Of[F[_]] = Functor[F] OrElse DerivedFunctor[F] - def map[A, B](fa: F[A])(f: A => B): F[B] = - inst.map(fa : F[A])( - [t[_]] => (func: T[t], t0: t[A]) => func.map(t0)(f) - ) + given [T]: DerivedFunctor[Const[T]] with + def map[A, B](fa: T)(f: A => B): T = fa + + def generic[F[_]](using K1.Instances[Of, F]): DerivedFunctor[F] = new Generic[Of, F] {} + inline given derived[F[_]](using K1.Generic[F]): DerivedFunctor[F] = generic + + trait Generic[T[x[_]] <: Of[x], F[_]](using inst: K1.Instances[T, F]) + extends DerivedFunctor[F]: + + def map[A, B](fa: F[A])(f: A => B): F[B] = + inst.map(fa: F[A]) { [f[_]] => (tf: T[f], fa: f[A]) => + tf.unify.map(fa)(f) + } trait FunctorDerivation: extension (F: Functor.type) - inline def derived[F[_]](using gen: K1.Generic[F]): Functor[F] = - new GenericFunctor[Functor, F]{} - - given [X]: Functor[Const[X]] with - def map[A, B](fa: X)(f: A => B): X = fa + def derived[F[_]](using instance: DerivedFunctor[F]): Functor[F] = instance diff --git a/core/src/main/scala-3/cats/derived/orelse.scala b/core/src/main/scala-3/cats/derived/orelse.scala new file mode 100644 index 00000000..6fc6a787 --- /dev/null +++ b/core/src/main/scala-3/cats/derived/orelse.scala @@ -0,0 +1,21 @@ +package cats.derived + +enum OrElse[+A, +B]: + case Primary(value: A) + case Secondary(value: () => B) + + final def fold[C](primary: A => C, secondary: B => C): C = this match + case Primary(value) => primary(value) + case Secondary(value) => secondary(value()) + + final def unify[C >: A](implicit ev: B <:< C): C = this match + case Primary(value) => value + case Secondary(value) => value() + +object OrElse extends OrElseLowPriority: + inline given primary[A, B](using inline a: A): OrElse[A, B] = + OrElse.Primary(a) + +private[derived] sealed abstract class OrElseLowPriority: + inline given secondary[A, B](using inline b: B): OrElse[A, B] = + OrElse.Secondary(() => b) diff --git a/core/src/main/scala-3/cats/derived/reducible.scala b/core/src/main/scala-3/cats/derived/reducible.scala index 4eb32d2b..894eb935 100644 --- a/core/src/main/scala-3/cats/derived/reducible.scala +++ b/core/src/main/scala-3/cats/derived/reducible.scala @@ -1,58 +1,61 @@ package cats.derived import cats.{Eval, Foldable, Reducible} -import shapeless3.deriving.{Continue, K1} +import shapeless3.deriving.{Continue, Const, K1} +import cats.derived.DerivedReducible.Coproduct object reducible extends ReducibleDerivation -trait ProductReducible[T[x[_]] <: Foldable[x], F[_]](ev: Any)(using inst: K1.ProductInstances[T, F]) - extends ProductFoldable[T, F], Reducible[F]: +trait DerivedReducible[F[_]] extends DerivedFoldable[F], Reducible[F] +object DerivedReducible: + type Of[F[_]] = Reducible[F] OrElse DerivedReducible[F] - private val none = Eval.now(None) + def product[F[_]](ev: Of[Const[Any]])(using K1.ProductInstances[DerivedFoldable.Of, F]): DerivedReducible[F] = + new Product[DerivedFoldable.Of, F](ev) {} - def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = - inst.foldLeft[A, Option[B]](fa)(None)( - [t[_]] => (acc: Option[B], fd: T[t], t0: t[A]) => - acc match - case Some(b) => Continue(Some(fd.foldLeft(t0, b)(g))) - case None => Continue(fd.reduceLeftToOption(t0)(f)(g)) - ).get + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedReducible[F] = + new Coproduct[Of, F] {} - def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - inst.foldRight[A, Eval[Option[B]]](fa)(none)( - [t[_]] => (fd: T[t], t0: t[A], acc: Eval[Option[B]]) => - Continue(acc.flatMap { - case Some(b) => fd.foldRight(t0, Eval.now(b))(g).map(Some.apply) - case None => fd.reduceRightToOption(t0)(f)(g) - }) - ).map(_.get) + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedReducible[F] = + inline gen match + case given K1.ProductGeneric[F] => product(K1.summonFirst[Of, gen.MirroredElemTypes, Const[Any]]) + case given K1.CoproductGeneric[F] => coproduct -object ProductReducible: - def instance[F[_]](ev: Any)(using gen: K1.ProductInstances[Foldable, F]): ProductReducible[Foldable, F] = - new ProductReducible[Foldable, F](ev){} + trait Product[T[x[_]] <: DerivedFoldable.Of[x], F[_]](ev: Of[Const[Any]])(using inst: K1.ProductInstances[T, F]) + extends DerivedFoldable.Product[T, F], DerivedReducible[F]: -trait CoproductReducible[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends CoproductFoldable[T, F], Reducible[F]: + private val none = Eval.now(None) - def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = - inst.fold[A, B](fa)( - [t[_]] => (fd: T[t], t0: t[A]) => fd.reduceLeftTo(t0)(f)(g) - ) + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = + inst.foldLeft[A, Option[B]](fa)(None)( + [f[_]] => (acc: Option[B], tf: T[f], fa: f[A]) => + acc match + case Some(b) => Continue(Some(tf.unify.foldLeft(fa, b)(g))) + case None => Continue(tf.unify.reduceLeftToOption(fa)(f)(g)) + ).get - def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - inst.fold[A, Eval[B]](fa)( - [t[_]] => (fd: T[t], t0: t[A]) => fd.reduceRightTo(t0)(f)(g) - ) + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.foldRight[A, Eval[Option[B]]](fa)(none)( + [f[_]] => (tf: T[f], fa: f[A], acc: Eval[Option[B]]) => + Continue(acc.flatMap { + case Some(b) => tf.unify.foldRight(fa, Eval.now(b))(g).map(Some.apply) + case None => tf.unify.reduceRightToOption(fa)(f)(g) + }) + ).map(_.get) -object CoproductReducible: - given instance[F[_]](using K1.CoproductInstances[Reducible, F]): CoproductReducible[Reducible, F] with {} + trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + extends DerivedFoldable.Coproduct[T, F], DerivedReducible[F]: + + def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = + inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) => + tf.unify.reduceLeftTo(fa)(f)(g) + } + + def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = + inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) => + Eval.defer(tf.unify.reduceRightTo(fa)(f)(g)) + } trait ReducibleDerivation: extension (F: Reducible.type) - inline def derived[F[_]](using gen: K1.Generic[F]): Reducible[F] = - inline gen match - case given K1.ProductGeneric[F] => - val ev = K1.summonFirst0[K1.LiftP[Reducible, gen.MirroredElemTypes]] - ProductReducible.instance(ev) - case given K1.CoproductGeneric[F] => CoproductReducible.instance - + def derived[F[_]](using instance: DerivedReducible[F]): Reducible[F] = instance diff --git a/core/src/main/scala-3/cats/derived/traverse.scala b/core/src/main/scala-3/cats/derived/traverse.scala index 34afc5d2..95575ad4 100644 --- a/core/src/main/scala-3/cats/derived/traverse.scala +++ b/core/src/main/scala-3/cats/derived/traverse.scala @@ -5,35 +5,50 @@ import shapeless3.deriving.{Const, Continue, K1} object traverse extends TraverseDerivation -trait ProductTraverse[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F]) - extends GenericFunctor[T, F], ProductFoldable[T, F], Traverse[F]: - - def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = - inst.traverse[A, G, B](fa)([a,b] => (ga: G[a], f: a => b) => G.map(ga)(f))([a] => (x: a) => G.pure(x))([a,b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga))( - [t[_]] => (trav: T[t], t0: t[A]) => trav.traverse[G, A, B](t0)(f) - ) - -trait CoproductTraverse[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends GenericFunctor[T, F], CoproductFoldable[T, F], Traverse[F]: - - def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = - inst.traverse[A, G, B](fa)([a,b] => (ga: G[a], f: a => b) => G.map(ga)(f))([a] => (x: a) => G.pure(x))([a,b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga))( - [t[_]] => (trav: T[t], t0: t[A]) => trav.traverse[G, A, B](t0)(f) - ) +trait DerivedTraverse[F[_]] extends DerivedFunctor[F], DerivedFoldable[F], Traverse[F] +object DerivedTraverse: + type Of[F[_]] = Traverse[F] OrElse DerivedTraverse[F] + + given [T]: DerivedTraverse[Const[T]] with + override def map[A, B](fa: T)(f: A => B): T = fa + def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b + def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb + def traverse[G[_], A, B](fa: T)(f: A => G[B])(using G: Applicative[G]): G[T] = G.pure(fa) + + def product[F[_]](using K1.ProductInstances[Of, F]): DerivedTraverse[F] = new Product[Of, F] {} + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedTraverse[F] = new Coproduct[Of, F] {} + + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedTraverse[F] = + gen.derive(product, coproduct) + + trait Product[T[x[_]] <: Of[x], F[_]](using inst: K1.ProductInstances[T, F]) + extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[T, F], DerivedTraverse[F]: + + def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = + inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) => + G.map(ga)(f) + } { [a] => (x: a) => + G.pure(x) + } { [a, b] => (gf: G[a => b], ga: G[a]) => + G.ap(gf)(ga) + } { [f[_]] => (tf: T[f], fa: f[A]) => + tf.unify.traverse(fa)(f) + } + + trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], DerivedTraverse[F]: + + def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = + inst.traverse[A, G, B](fa) { [a, b] => (ga: G[a], f: a => b) => + G.map(ga)(f) + } { [a] => (x: a) => + G.pure(x) + } { [a, b] => (gf: G[a => b], ga: G[a]) => + G.ap(gf)(ga) + } { [f[_]] => (tf: T[f], fa: f[A]) => + tf.unify.traverse(fa)(f) + } trait TraverseDerivation: extension (F: Traverse.type) - inline def derived[F[_]](using gen: K1.Generic[F]): Traverse[F] = - gen.derive(productTraverse, coproductTraverse) - - given productTraverse[F[_]](using inst: => K1.ProductInstances[Traverse, F]): Traverse[F] = - new ProductTraverse[Traverse, F]{} - - given coproductTraverse[F[_]](using inst: => K1.CoproductInstances[Traverse, F]): Traverse[F] = - new CoproductTraverse[Traverse, F]{} - - given [X]: Traverse[Const[X]] with - override def map[A, B](fa: X)(f: A => B): X = fa - def foldLeft[A, B](fa: X, b: B)(f: (B, A) => B): B = b - def foldRight[A, B](fa: X, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb - def traverse[G[_]: Applicative, A, B](fa: X)(f: A => G[B]): G[X] = Applicative[G].pure(fa) + def derived[F[_]](using instance: DerivedTraverse[F]): Traverse[F] = instance diff --git a/core/src/test/scala-3/cats/derived/FoldableTests.scala b/core/src/test/scala-3/cats/derived/FoldableTests.scala index 9d892b03..8d44ed1d 100644 --- a/core/src/test/scala-3/cats/derived/FoldableTests.scala +++ b/core/src/test/scala-3/cats/derived/FoldableTests.scala @@ -2,7 +2,7 @@ package cats.derived import cats.Foldable import cats.derived.all._ -import cats.derived.all.given +import cats.instances.all._ class FoldableTests { @@ -14,5 +14,5 @@ class FoldableTests { sealed trait CList[A] derives Foldable case object CNil extends CList[Nothing] - case class CCons[A](head: A, tail: CCons[A]) extends CList[A] + case class CCons[A](head: A, tail: CList[A]) extends CList[A] } diff --git a/core/src/test/scala-3/cats/derived/FunctorTests.scala b/core/src/test/scala-3/cats/derived/FunctorTests.scala index ed20a073..5b7c59c1 100644 --- a/core/src/test/scala-3/cats/derived/FunctorTests.scala +++ b/core/src/test/scala-3/cats/derived/FunctorTests.scala @@ -14,5 +14,5 @@ class FunctorTests { sealed trait CList[A] derives Functor case object CNil extends CList[Nothing] - case class CCons[A](head: A, tail: CCons[A]) extends CList[A] + case class CCons[A](head: A, tail: CList[A]) extends CList[A] } diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index 53261f61..c029af29 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -3,21 +3,24 @@ package cats.derived import cats.Reducible import cats.data.NonEmptyList import cats.derived.all._ -import cats.derived.all.given +import cats.instances.all._ class ReducibleTests { case class Box[A](value: A) derives Reducible - sealed trait OneOrMany[+A] + sealed trait OneOrMany[+A] derives Reducible case class One[+A](value: A) extends OneOrMany[A] case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] - Reducible.derived[One] - Reducible.derived[Many] - Reducible.derived[OneOrMany] + sealed trait CList[A] derives Reducible + case class COne[A](value: A) extends CList[A] + case class CCons[A](head: A, tail: CList[A]) extends CList[A] - sealed trait CList[A] - case object CNil extends CList[Nothing] - case class CCons[A](head: A, tail: CCons[A]) extends CList[A] + case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) + + case class Foo[A](value: A, xs: List[A]) + sealed trait Tree[+A] derives Reducible + case class Leaf[A](value: A) extends Tree[A] + case class Node[A](left: Tree[A], values: Foo[A], right: Tree[A]) extends Tree[A] } diff --git a/core/src/test/scala-3/cats/derived/TraverseTests.scala b/core/src/test/scala-3/cats/derived/TraverseTests.scala index 6652eef3..97ed3165 100644 --- a/core/src/test/scala-3/cats/derived/TraverseTests.scala +++ b/core/src/test/scala-3/cats/derived/TraverseTests.scala @@ -14,5 +14,5 @@ class TraverseTests { sealed trait CList[A] derives Traverse case object CNil extends CList[Nothing] - case class CCons[A](head: A, tail: CCons[A]) extends CList[A] + case class CCons[A](head: A, tail: CList[A]) extends CList[A] } From 64d066fb99807eaf08320bd8f5e8bdd7a2c3b9ca Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Wed, 26 May 2021 16:05:17 +0300 Subject: [PATCH 3/6] Reproduce problem for recursive type --- core/src/main/scala-3/cats/derived/orelse.scala | 14 +++++++------- .../test/scala-3/cats/derived/ReducibleTests.scala | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/orelse.scala b/core/src/main/scala-3/cats/derived/orelse.scala index 6fc6a787..4d274919 100644 --- a/core/src/main/scala-3/cats/derived/orelse.scala +++ b/core/src/main/scala-3/cats/derived/orelse.scala @@ -1,5 +1,7 @@ package cats.derived +import scala.compiletime._ + enum OrElse[+A, +B]: case Primary(value: A) case Secondary(value: () => B) @@ -12,10 +14,8 @@ enum OrElse[+A, +B]: case Primary(value) => value case Secondary(value) => value() -object OrElse extends OrElseLowPriority: - inline given primary[A, B](using inline a: A): OrElse[A, B] = - OrElse.Primary(a) - -private[derived] sealed abstract class OrElseLowPriority: - inline given secondary[A, B](using inline b: B): OrElse[A, B] = - OrElse.Secondary(() => b) +object OrElse: + inline given [A, B]: OrElse[A, B] = summonFrom { + case a: A => Primary(a) + case b: B => Secondary(() => b) + } diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index c029af29..714548d2 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -19,8 +19,9 @@ class ReducibleTests { case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) + summon[DerivedFoldable[Tree]] case class Foo[A](value: A, xs: List[A]) - sealed trait Tree[+A] derives Reducible + sealed trait Tree[+A] case class Leaf[A](value: A) extends Tree[A] case class Node[A](left: Tree[A], values: Foo[A], right: Tree[A]) extends Tree[A] } From 4ba722b2c4dbeb8b867c54a0674db0e9bca134bc Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Wed, 26 May 2021 20:20:55 +0300 Subject: [PATCH 4/6] Simplifications --- .../main/scala-3/cats/derived/foldable.scala | 6 ++-- .../main/scala-3/cats/derived/orelse.scala | 19 +++++------- .../scala-3/cats/derived/ReducibleTests.scala | 31 ++++++++++--------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 522994ab..47bbb8c2 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -13,8 +13,8 @@ object DerivedFoldable: def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb - def product[F[_]](using inst: => K1.ProductInstances[Of, F]): DerivedFoldable[F] = new Product[Of, F] {} - def coproduct[F[_]](using inst: => K1.CoproductInstances[Of, F]): DerivedFoldable[F] = new Coproduct[Of, F] {} + def product[F[_]](using K1.ProductInstances[Of, F]): DerivedFoldable[F] = new Product[Of, F] {} + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedFoldable[F] = new Coproduct[Of, F] {} inline given derived[F[_]](using gen: K1.Generic[F]): DerivedFoldable[F] = gen.derive(product, coproduct) @@ -47,4 +47,4 @@ object DerivedFoldable: trait FoldableDerivation: extension (F: Foldable.type) - def derived[F[_]](using instance: => DerivedFoldable[F]): Foldable[F] = instance + def derived[F[_]](using instance: DerivedFoldable[F]): Foldable[F] = instance diff --git a/core/src/main/scala-3/cats/derived/orelse.scala b/core/src/main/scala-3/cats/derived/orelse.scala index 4d274919..a5b34a1a 100644 --- a/core/src/main/scala-3/cats/derived/orelse.scala +++ b/core/src/main/scala-3/cats/derived/orelse.scala @@ -2,20 +2,15 @@ package cats.derived import scala.compiletime._ -enum OrElse[+A, +B]: - case Primary(value: A) - case Secondary(value: () => B) - - final def fold[C](primary: A => C, secondary: B => C): C = this match - case Primary(value) => primary(value) - case Secondary(value) => secondary(value()) - - final def unify[C >: A](implicit ev: B <:< C): C = this match - case Primary(value) => value - case Secondary(value) => value() +sealed trait OrElse[+A, +B]: + def unify: A | B object OrElse: + final class Primary[+A](val unify: A) extends OrElse[A, Nothing] + final class Secondary[+B](value: => B) extends OrElse[Nothing, B]: + lazy val unify: B = value + inline given [A, B]: OrElse[A, B] = summonFrom { case a: A => Primary(a) - case b: B => Secondary(() => b) + case b: B => Secondary(b) } diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index 714548d2..5f01820c 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -4,24 +4,27 @@ import cats.Reducible import cats.data.NonEmptyList import cats.derived.all._ import cats.instances.all._ +import scala.compiletime._ +import shapeless3.deriving.K1 -class ReducibleTests { +object ReducibleTests { - case class Box[A](value: A) derives Reducible + // case class Box[A](value: A) derives Reducible - sealed trait OneOrMany[+A] derives Reducible - case class One[+A](value: A) extends OneOrMany[A] - case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] + // sealed trait OneOrMany[+A] derives Reducible + // case class One[+A](value: A) extends OneOrMany[A] + // case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] - sealed trait CList[A] derives Reducible - case class COne[A](value: A) extends CList[A] - case class CCons[A](head: A, tail: CList[A]) extends CList[A] + // sealed trait CList[A] derives Reducible + // case class COne[A](value: A) extends CList[A] + // case class CCons[A](head: A, tail: CList[A]) extends CList[A] - case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) + // case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) - summon[DerivedFoldable[Tree]] - case class Foo[A](value: A, xs: List[A]) - sealed trait Tree[+A] - case class Leaf[A](value: A) extends Tree[A] - case class Node[A](left: Tree[A], values: Foo[A], right: Tree[A]) extends Tree[A] + // case class Foo[A](value: A, xs: List[A]) + enum MyList[+A]: + case Non + case Con(v: A, r: MyList[A]) + + summonInline[DerivedFunctor[MyList]] } From b0bc1fcb428afc5f092024b6b3fdeac696fa16d7 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Mon, 31 May 2021 17:48:17 +0300 Subject: [PATCH 5/6] Use Delegated Functor, Foldable, Reducible, Traverse --- .../main/scala-3/cats/derived/foldable.scala | 41 +++++++++----- .../main/scala-3/cats/derived/functor.scala | 29 +++++++--- .../main/scala-3/cats/derived/orelse.scala | 16 ------ .../main/scala-3/cats/derived/reducible.scala | 55 +++++++++++-------- .../main/scala-3/cats/derived/traverse.scala | 39 ++++++++----- .../scala-3/cats/derived/ReducibleTests.scala | 28 +++++----- 6 files changed, 122 insertions(+), 86 deletions(-) delete mode 100644 core/src/main/scala-3/cats/derived/orelse.scala diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 47bbb8c2..2f4be05f 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -2,49 +2,62 @@ package cats.derived import cats.{Eval, Foldable} import shapeless3.deriving.{Const, Continue, K1} +import scala.annotation.threadUnsafe object foldable extends FoldableDerivation, Instances trait DerivedFoldable[F[_]] extends Foldable[F] -object DerivedFoldable: - type Of[F[_]] = Foldable[F] OrElse DerivedFoldable[F] - - given [T]: DerivedFoldable[Const[T]] with +object DerivedFoldable extends DerivedFoldableLowPriority: + given const[T]: DerivedFoldable[Const[T]] with def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb - def product[F[_]](using K1.ProductInstances[Of, F]): DerivedFoldable[F] = new Product[Of, F] {} - def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedFoldable[F] = new Coproduct[Of, F] {} + given delegated[F[_]](using F: => Foldable[F]): DerivedFoldable[F] = + new Delegated(F) - inline given derived[F[_]](using gen: K1.Generic[F]): DerivedFoldable[F] = - gen.derive(product, coproduct) + given composed[F[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] = + new Delegated(Foldable[F].compose[G]) + + def product[F[_]](using K1.ProductInstances[DerivedFoldable, F]): DerivedFoldable[F] = + new Product[DerivedFoldable, F] {} + + def coproduct[F[_]](using K1.CoproductInstances[DerivedFoldable, F]): DerivedFoldable[F] = + new Coproduct[DerivedFoldable, F] {} - trait Product[T[x[_]] <: Of[x], F[_]](using inst: K1.ProductInstances[T, F]) + trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F]) extends DerivedFoldable[F]: def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = inst.foldLeft[A, B](fa)(b) { [f[_]] => (acc: B, tf: T[f], fa: f[A]) => - Continue(tf.unify.foldLeft(fa, acc)(f)) + Continue(tf.foldLeft(fa, acc)(f)) } def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = inst.foldRight[A, Eval[B]](fa)(lb) { [f[_]] => (tf: T[f], fa: f[A], acc: Eval[B]) => - Continue(Eval.defer(tf.unify.foldRight(fa, acc)(f))) + Continue(Eval.defer(tf.foldRight(fa, acc)(f))) } - trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) extends DerivedFoldable[F]: def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B = inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) => - tf.unify.foldLeft(fa, b)(f) + tf.foldLeft(fa, b)(f) } def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) => - Eval.defer(tf.unify.foldRight(fa, lb)(f)) + Eval.defer(tf.foldRight(fa, lb)(f)) } + private final class Delegated[F[_]](F: => Foldable[F]) extends DerivedFoldable[F]: + @threadUnsafe private lazy val underlying = F + export underlying._ + +private[derived] sealed abstract class DerivedFoldableLowPriority: + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedFoldable[F] = + gen.derive(DerivedFoldable.product, DerivedFoldable.coproduct) + trait FoldableDerivation: extension (F: Foldable.type) def derived[F[_]](using instance: DerivedFoldable[F]): Foldable[F] = instance diff --git a/core/src/main/scala-3/cats/derived/functor.scala b/core/src/main/scala-3/cats/derived/functor.scala index bc0df107..b87f0051 100644 --- a/core/src/main/scala-3/cats/derived/functor.scala +++ b/core/src/main/scala-3/cats/derived/functor.scala @@ -2,27 +2,40 @@ package cats.derived import cats.Functor import shapeless3.deriving.{Const, K1} +import scala.annotation.threadUnsafe object functor extends FunctorDerivation, Instances trait DerivedFunctor[F[_]] extends Functor[F] -object DerivedFunctor: - type Of[F[_]] = Functor[F] OrElse DerivedFunctor[F] - - given [T]: DerivedFunctor[Const[T]] with +object DerivedFunctor extends DerivedFunctorLowPriority: + given const[T]: DerivedFunctor[Const[T]] with def map[A, B](fa: T)(f: A => B): T = fa - def generic[F[_]](using K1.Instances[Of, F]): DerivedFunctor[F] = new Generic[Of, F] {} - inline given derived[F[_]](using K1.Generic[F]): DerivedFunctor[F] = generic + given delegated[F[_]](using F: => Functor[F]): DerivedFunctor[F] = + new Delegated(F) + + given composed[F[_]: DerivedFunctor, G[_]: DerivedFunctor]: DerivedFunctor[[x] =>> F[G[x]]] = + new Delegated(Functor[F].compose[G]) + + def generic[F[_]](using K1.Instances[DerivedFunctor, F]): DerivedFunctor[F] = + new Generic[DerivedFunctor, F] {} - trait Generic[T[x[_]] <: Of[x], F[_]](using inst: K1.Instances[T, F]) + trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F]) extends DerivedFunctor[F]: def map[A, B](fa: F[A])(f: A => B): F[B] = inst.map(fa: F[A]) { [f[_]] => (tf: T[f], fa: f[A]) => - tf.unify.map(fa)(f) + tf.map(fa)(f) } + private final class Delegated[F[_]](F: => Functor[F]) extends DerivedFunctor[F]: + @threadUnsafe private lazy val underlying = F + export underlying._ + +private[derived] sealed abstract class DerivedFunctorLowPriority: + inline given derived[F[_]](using K1.Generic[F]): DerivedFunctor[F] = + DerivedFunctor.generic + trait FunctorDerivation: extension (F: Functor.type) def derived[F[_]](using instance: DerivedFunctor[F]): Functor[F] = instance diff --git a/core/src/main/scala-3/cats/derived/orelse.scala b/core/src/main/scala-3/cats/derived/orelse.scala deleted file mode 100644 index a5b34a1a..00000000 --- a/core/src/main/scala-3/cats/derived/orelse.scala +++ /dev/null @@ -1,16 +0,0 @@ -package cats.derived - -import scala.compiletime._ - -sealed trait OrElse[+A, +B]: - def unify: A | B - -object OrElse: - final class Primary[+A](val unify: A) extends OrElse[A, Nothing] - final class Secondary[+B](value: => B) extends OrElse[Nothing, B]: - lazy val unify: B = value - - inline given [A, B]: OrElse[A, B] = summonFrom { - case a: A => Primary(a) - case b: B => Secondary(b) - } diff --git a/core/src/main/scala-3/cats/derived/reducible.scala b/core/src/main/scala-3/cats/derived/reducible.scala index 894eb935..83f47c57 100644 --- a/core/src/main/scala-3/cats/derived/reducible.scala +++ b/core/src/main/scala-3/cats/derived/reducible.scala @@ -2,60 +2,71 @@ package cats.derived import cats.{Eval, Foldable, Reducible} import shapeless3.deriving.{Continue, Const, K1} -import cats.derived.DerivedReducible.Coproduct +import scala.annotation.threadUnsafe object reducible extends ReducibleDerivation -trait DerivedReducible[F[_]] extends DerivedFoldable[F], Reducible[F] -object DerivedReducible: - type Of[F[_]] = Reducible[F] OrElse DerivedReducible[F] +trait DerivedReducible[F[_]] extends Reducible[F] +object DerivedReducible extends DerivedReducibleLowPriority: + given delegated[F[_]](using F: => Reducible[F]): DerivedReducible[F] = + new Delegated(F) - def product[F[_]](ev: Of[Const[Any]])(using K1.ProductInstances[DerivedFoldable.Of, F]): DerivedReducible[F] = - new Product[DerivedFoldable.Of, F](ev) {} + given composed[F[_]: DerivedReducible, G[_]: DerivedReducible]: DerivedReducible[[x] =>> F[G[x]]] = + new Delegated(Reducible[F].compose[G]) - def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedReducible[F] = - new Coproduct[Of, F] {} + def product[F[_]](ev: Reducible[?])(using K1.ProductInstances[DerivedFoldable, F]): DerivedReducible[F] = + new Product[DerivedFoldable, F](ev) {} - inline given derived[F[_]](using gen: K1.Generic[F]): DerivedReducible[F] = - inline gen match - case given K1.ProductGeneric[F] => product(K1.summonFirst[Of, gen.MirroredElemTypes, Const[Any]]) - case given K1.CoproductGeneric[F] => coproduct + def coproduct[F[_]](using K1.CoproductInstances[DerivedReducible, F]): DerivedReducible[F] = + new Coproduct[DerivedReducible, F] {} - trait Product[T[x[_]] <: DerivedFoldable.Of[x], F[_]](ev: Of[Const[Any]])(using inst: K1.ProductInstances[T, F]) + trait Product[T[x[_]] <: Foldable[x], F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[T, F]) extends DerivedFoldable.Product[T, F], DerivedReducible[F]: - private val none = Eval.now(None) + private val evalNone = Eval.now(None) def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = inst.foldLeft[A, Option[B]](fa)(None)( [f[_]] => (acc: Option[B], tf: T[f], fa: f[A]) => acc match - case Some(b) => Continue(Some(tf.unify.foldLeft(fa, b)(g))) - case None => Continue(tf.unify.reduceLeftToOption(fa)(f)(g)) + case Some(b) => Continue(Some(tf.foldLeft(fa, b)(g))) + case None => Continue(tf.reduceLeftToOption(fa)(f)(g)) ).get def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = - inst.foldRight[A, Eval[Option[B]]](fa)(none)( + inst.foldRight[A, Eval[Option[B]]](fa)(evalNone)( [f[_]] => (tf: T[f], fa: f[A], acc: Eval[Option[B]]) => Continue(acc.flatMap { - case Some(b) => tf.unify.foldRight(fa, Eval.now(b))(g).map(Some.apply) - case None => tf.unify.reduceRightToOption(fa)(f)(g) + case Some(b) => tf.foldRight(fa, Eval.now(b))(g).map(Some.apply) + case None => tf.reduceRightToOption(fa)(f)(g) }) ).map(_.get) - trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + trait Coproduct[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F]) extends DerivedFoldable.Coproduct[T, F], DerivedReducible[F]: def reduceLeftTo[A, B](fa: F[A])(f: A => B)(g: (B, A) => B): B = inst.fold[A, B](fa) { [f[_]] => (tf: T[f], fa: f[A]) => - tf.unify.reduceLeftTo(fa)(f)(g) + tf.reduceLeftTo(fa)(f)(g) } def reduceRightTo[A, B](fa: F[A])(f: A => B)(g: (A, Eval[B]) => Eval[B]): Eval[B] = inst.fold[A, Eval[B]](fa) { [f[_]] => (tf: T[f], fa: f[A]) => - Eval.defer(tf.unify.reduceRightTo(fa)(f)(g)) + Eval.defer(tf.reduceRightTo(fa)(f)(g)) } + private final class Delegated[F[_]](F: => Reducible[F]) extends DerivedReducible[F]: + @threadUnsafe private lazy val underlying = F + export underlying._ + +private[derived] sealed abstract class DerivedReducibleLowPriority: + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedReducible[F] = + inline gen match + case given K1.ProductGeneric[F] => + DerivedReducible.product(K1.summonFirst[DerivedReducible, gen.MirroredElemTypes, Const[Any]]) + case given K1.CoproductGeneric[F] => + DerivedReducible.coproduct + trait ReducibleDerivation: extension (F: Reducible.type) def derived[F[_]](using instance: DerivedReducible[F]): Reducible[F] = instance diff --git a/core/src/main/scala-3/cats/derived/traverse.scala b/core/src/main/scala-3/cats/derived/traverse.scala index 687b9556..866666ba 100644 --- a/core/src/main/scala-3/cats/derived/traverse.scala +++ b/core/src/main/scala-3/cats/derived/traverse.scala @@ -2,26 +2,31 @@ package cats.derived import cats.{Applicative, Eval, Traverse} import shapeless3.deriving.{Const, Continue, K1} +import scala.annotation.threadUnsafe object traverse extends TraverseDerivation, Instances -trait DerivedTraverse[F[_]] extends DerivedFunctor[F], DerivedFoldable[F], Traverse[F] -object DerivedTraverse: - type Of[F[_]] = Traverse[F] OrElse DerivedTraverse[F] - - given [T]: DerivedTraverse[Const[T]] with +trait DerivedTraverse[F[_]] extends Traverse[F] +object DerivedTraverse extends DerivedTraverseLowPriority: + given const[T]: DerivedTraverse[Const[T]] with override def map[A, B](fa: T)(f: A => B): T = fa def foldLeft[A, B](fa: T, b: B)(f: (B, A) => B): B = b def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb def traverse[G[_], A, B](fa: T)(f: A => G[B])(using G: Applicative[G]): G[T] = G.pure(fa) - def product[F[_]](using K1.ProductInstances[Of, F]): DerivedTraverse[F] = new Product[Of, F] {} - def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedTraverse[F] = new Coproduct[Of, F] {} + given delegated[F[_]](using F: => Traverse[F]): DerivedTraverse[F] = + new Delegated(F) - inline given derived[F[_]](using gen: K1.Generic[F]): DerivedTraverse[F] = - gen.derive(product, coproduct) + given composed[F[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] = + new Delegated(Traverse[F].compose[G]) + + def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] = + new Product[DerivedTraverse, F] {} + + def coproduct[F[_]](using K1.CoproductInstances[DerivedTraverse, F]): DerivedTraverse[F] = + new Coproduct[DerivedTraverse, F] {} - trait Product[T[x[_]] <: Of[x], F[_]](using inst: K1.ProductInstances[T, F]) + trait Product[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F]) extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[T, F], DerivedTraverse[F]: def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = @@ -32,10 +37,10 @@ object DerivedTraverse: } { [a, b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga) } { [f[_]] => (tf: T[f], fa: f[A]) => - tf.unify.traverse(fa)(f) + tf.traverse(fa)(f) } - trait Coproduct[T[x[_]] <: Of[x], F[_]](using inst: K1.CoproductInstances[T, F]) + trait Coproduct[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F]) extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], DerivedTraverse[F]: def traverse[G[_], A, B](fa: F[A])(f: A => G[B])(using G: Applicative[G]): G[F[B]] = @@ -46,9 +51,17 @@ object DerivedTraverse: } { [a, b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga) } { [f[_]] => (tf: T[f], fa: f[A]) => - tf.unify.traverse(fa)(f) + tf.traverse(fa)(f) } + private final class Delegated[F[_]](F: => Traverse[F]) extends DerivedTraverse[F]: + @threadUnsafe private lazy val underlying = F + export underlying._ + +private[derived] sealed abstract class DerivedTraverseLowPriority: + inline given derived[F[_]](using gen: K1.Generic[F]): DerivedTraverse[F] = + gen.derive(DerivedTraverse.product, DerivedTraverse.coproduct) + trait TraverseDerivation: extension (F: Traverse.type) def derived[F[_]](using instance: DerivedTraverse[F]): Traverse[F] = instance diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index 5f01820c..f6c59114 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -4,27 +4,29 @@ import cats.Reducible import cats.data.NonEmptyList import cats.derived.all._ import cats.instances.all._ -import scala.compiletime._ -import shapeless3.deriving.K1 -object ReducibleTests { +class ReducibleTests { - // case class Box[A](value: A) derives Reducible + case class Box[A](value: A) derives Reducible - // sealed trait OneOrMany[+A] derives Reducible - // case class One[+A](value: A) extends OneOrMany[A] - // case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] + sealed trait OneOrMany[+A] derives Reducible + case class One[+A](value: A) extends OneOrMany[A] + case class Many[+A](values: NonEmptyList[A]) extends OneOrMany[A] - // sealed trait CList[A] derives Reducible - // case class COne[A](value: A) extends CList[A] - // case class CCons[A](head: A, tail: CList[A]) extends CList[A] + sealed trait CList[A] derives Reducible + case class COne[A](value: A) extends CList[A] + case class CCons[A](head: A, tail: CList[A]) extends CList[A] - // case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) + case class NonEmptyTree[A](size: Int, value: A, tree: Tree[A]) + enum Tree[+A]: + case Leaf + case Node(left: Tree[A], value: A, right: Tree[A]) - // case class Foo[A](value: A, xs: List[A]) + case class Foo[A](value: A, xs: List[A]) enum MyList[+A]: case Non case Con(v: A, r: MyList[A]) - summonInline[DerivedFunctor[MyList]] + summon[DerivedFunctor[MyList]] + summon[DerivedReducible[NonEmptyTree]] } From 198119a4f7b54380958ee2525c6d773498a46469 Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Tue, 1 Jun 2021 12:40:49 +0300 Subject: [PATCH 6/6] Remove unused imports --- core/src/test/scala-3/cats/derived/FoldableTests.scala | 1 - core/src/test/scala-3/cats/derived/FunctorTests.scala | 1 - core/src/test/scala-3/cats/derived/ReducibleTests.scala | 1 - core/src/test/scala-3/cats/derived/TraverseTests.scala | 1 - 4 files changed, 4 deletions(-) diff --git a/core/src/test/scala-3/cats/derived/FoldableTests.scala b/core/src/test/scala-3/cats/derived/FoldableTests.scala index 8d44ed1d..bd88fca4 100644 --- a/core/src/test/scala-3/cats/derived/FoldableTests.scala +++ b/core/src/test/scala-3/cats/derived/FoldableTests.scala @@ -2,7 +2,6 @@ package cats.derived import cats.Foldable import cats.derived.all._ -import cats.instances.all._ class FoldableTests { diff --git a/core/src/test/scala-3/cats/derived/FunctorTests.scala b/core/src/test/scala-3/cats/derived/FunctorTests.scala index 5b7c59c1..6943902a 100644 --- a/core/src/test/scala-3/cats/derived/FunctorTests.scala +++ b/core/src/test/scala-3/cats/derived/FunctorTests.scala @@ -2,7 +2,6 @@ package cats.derived import cats.Functor import cats.derived.all._ -import cats.derived.all.given class FunctorTests { diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index f6c59114..d327918e 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -3,7 +3,6 @@ package cats.derived import cats.Reducible import cats.data.NonEmptyList import cats.derived.all._ -import cats.instances.all._ class ReducibleTests { diff --git a/core/src/test/scala-3/cats/derived/TraverseTests.scala b/core/src/test/scala-3/cats/derived/TraverseTests.scala index 97ed3165..688408ed 100644 --- a/core/src/test/scala-3/cats/derived/TraverseTests.scala +++ b/core/src/test/scala-3/cats/derived/TraverseTests.scala @@ -2,7 +2,6 @@ package cats.derived import cats.Traverse import cats.derived.all._ -import cats.derived.all.given class TraverseTests {