From 34b3076ccb82f1f860b429e8ad167e67169842cd Mon Sep 17 00:00:00 2001 From: Georgi Krastev Date: Wed, 2 Jun 2021 16:45:46 +0300 Subject: [PATCH] Experiment with OrElse --- .../src/main/scala-3/cats/derived/empty.scala | 18 +++--- .../main/scala-3/cats/derived/foldable.scala | 45 +++++++------- .../main/scala-3/cats/derived/functor.scala | 33 +++++------ .../main/scala-3/cats/derived/orelse.scala | 17 ++++++ .../main/scala-3/cats/derived/reducible.scala | 58 +++++++++---------- .../main/scala-3/cats/derived/traverse.scala | 43 +++++++------- .../scala-3/cats/derived/EmptyTests.scala | 5 +- .../scala-3/cats/derived/ReducibleTests.scala | 2 +- 8 files changed, 113 insertions(+), 108 deletions(-) create mode 100644 core/src/main/scala-3/cats/derived/orelse.scala diff --git a/core/src/main/scala-3/cats/derived/empty.scala b/core/src/main/scala-3/cats/derived/empty.scala index 0260d426..ec3c4895 100644 --- a/core/src/main/scala-3/cats/derived/empty.scala +++ b/core/src/main/scala-3/cats/derived/empty.scala @@ -2,7 +2,7 @@ package cats.derived import alleycats.Empty import shapeless3.deriving.K0 -import scala.compiletime.summonFrom +import scala.compiletime.* object empty extends EmptyDerivation @@ -11,20 +11,18 @@ trait DerivedEmpty[A] extends Empty[A]: lazy val empty: A = emptyValue() object DerivedEmpty: - inline given [A]: DerivedEmpty[A] = summonFrom { - case given Empty[A] => delegated - case given K0.ProductInstances[DerivedEmpty, A] => product + type Of[A] = Empty[A] OrElse DerivedEmpty[A] + + inline given derived[A]: DerivedEmpty[A] = summonFrom { + case given K0.ProductInstances[Of, A] => product case given K0.CoproductGeneric[A] => coproduct } - - def delegated[A](using A: => Empty[A]): DerivedEmpty[A] = - () => A.empty - def product[A](using inst: K0.ProductInstances[DerivedEmpty, A]): DerivedEmpty[A] = - () => inst.construct([A] => (A: DerivedEmpty[A]) => A.empty) + def product[A](using inst: K0.ProductInstances[Of, A]): DerivedEmpty[A] = + () => inst.construct([A] => (A: Of[A]) => A.unify.empty) inline def coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] = - K0.summonFirst[DerivedEmpty, gen.MirroredElemTypes, A] + () => K0.summonFirst[Of, gen.MirroredElemTypes, A].unify.empty trait EmptyDerivation: extension (E: Empty.type) diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 2f4be05f..0cf283a9 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -2,62 +2,59 @@ package cats.derived import cats.{Eval, Foldable} import shapeless3.deriving.{Const, Continue, K1} -import scala.annotation.threadUnsafe +import scala.compiletime.* object foldable extends FoldableDerivation, Instances trait DerivedFoldable[F[_]] extends Foldable[F] -object DerivedFoldable extends DerivedFoldableLowPriority: +object DerivedFoldable: + type Of[F[_]] = Alt1[Foldable, DerivedFoldable, F] + 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 - given delegated[F[_]](using F: => Foldable[F]): DerivedFoldable[F] = - new Delegated(F) + given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedFoldable[[x] =>> F[G[x]]] with + private val underlying = F.unify `compose` G.unify + export underlying._ - given composed[F[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] = - new Delegated(Foldable[F].compose[G]) + inline given derived[F[_]]: DerivedFoldable[F] = summonFrom { + case given K1.ProductInstances[Of, F] => product + case given K1.CoproductInstances[Of, F] => coproduct + } - def product[F[_]](using K1.ProductInstances[DerivedFoldable, F]): DerivedFoldable[F] = - new Product[DerivedFoldable, F] {} + def product[F[_]](using K1.ProductInstances[Of, F]): DerivedFoldable[F] = + new Product[Of, F] {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedFoldable, F]): DerivedFoldable[F] = - new Coproduct[DerivedFoldable, F] {} + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedFoldable[F] = + new Coproduct[Of, F] {} - trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, 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.foldLeft[A, B](fa)(b) { [f[_]] => (acc: B, tf: T[f], fa: f[A]) => - Continue(tf.foldLeft(fa, acc)(f)) + 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.foldRight[A, Eval[B]](fa)(lb) { [f[_]] => (tf: T[f], fa: f[A], acc: Eval[B]) => - Continue(Eval.defer(tf.foldRight(fa, acc)(f))) + Continue(Eval.defer(tf.unify.foldRight(fa, acc)(f))) } - trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) + 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.foldLeft(fa, b)(f) + 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.foldRight(fa, lb)(f)) + Eval.defer(tf.unify.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 b87f0051..b7d3233e 100644 --- a/core/src/main/scala-3/cats/derived/functor.scala +++ b/core/src/main/scala-3/cats/derived/functor.scala @@ -2,40 +2,37 @@ package cats.derived import cats.Functor import shapeless3.deriving.{Const, K1} -import scala.annotation.threadUnsafe +import scala.compiletime.* object functor extends FunctorDerivation, Instances trait DerivedFunctor[F[_]] extends Functor[F] -object DerivedFunctor extends DerivedFunctorLowPriority: +object DerivedFunctor: + type Of[F[_]] = Alt1[Functor, DerivedFunctor, F] + given const[T]: DerivedFunctor[Const[T]] with def map[A, B](fa: T)(f: A => B): T = fa - given delegated[F[_]](using F: => Functor[F]): DerivedFunctor[F] = - new Delegated(F) + given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedFunctor[[x] =>> F[G[x]]] with + private val underlying = F.unify `compose` G.unify + export underlying._ - given composed[F[_]: DerivedFunctor, G[_]: DerivedFunctor]: DerivedFunctor[[x] =>> F[G[x]]] = - new Delegated(Functor[F].compose[G]) + inline given derived[F[_]]: DerivedFunctor[F] = summonFrom { + case given K1.ProductInstances[Of, F] => generic + case given K1.CoproductInstances[Of, F] => generic + } - def generic[F[_]](using K1.Instances[DerivedFunctor, F]): DerivedFunctor[F] = - new Generic[DerivedFunctor, F] {} + def generic[F[_]](using K1.Instances[Of, F]): DerivedFunctor[F] = + new Generic[Of, F] {} - trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F]) + 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.map(fa)(f) + tf.unify.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 new file mode 100644 index 00000000..ec88945a --- /dev/null +++ b/core/src/main/scala-3/cats/derived/orelse.scala @@ -0,0 +1,17 @@ +package cats.derived + +import scala.compiletime._ + +final case class OrElse[+A, +B](unify: A | B) +object OrElse: + inline given [A, B]: (A OrElse B) = summonFrom { + case a: A => OrElse(a) + case b: B => OrElse(b) + } + +final case class Alt1[+A[_[_]], +B[_[_]], F[_]](unify: A[F] | B[F]) +object Alt1: + inline given [A[_[_]], B[_[_]], F[_]]: Alt1[A, B, F] = summonFrom { + case af: A[F] => Alt1(af) + case bf: B[F] => Alt1(bf) + } diff --git a/core/src/main/scala-3/cats/derived/reducible.scala b/core/src/main/scala-3/cats/derived/reducible.scala index 83f47c57..8813a610 100644 --- a/core/src/main/scala-3/cats/derived/reducible.scala +++ b/core/src/main/scala-3/cats/derived/reducible.scala @@ -2,25 +2,33 @@ package cats.derived import cats.{Eval, Foldable, Reducible} import shapeless3.deriving.{Continue, Const, K1} -import scala.annotation.threadUnsafe +import scala.compiletime.* object reducible extends ReducibleDerivation -trait DerivedReducible[F[_]] extends Reducible[F] -object DerivedReducible extends DerivedReducibleLowPriority: - given delegated[F[_]](using F: => Reducible[F]): DerivedReducible[F] = - new Delegated(F) +trait DerivedReducible[F[_]] extends DerivedFoldable[F], Reducible[F] +object DerivedReducible: + type Of[F[_]] = Alt1[Reducible, DerivedReducible, F] - given composed[F[_]: DerivedReducible, G[_]: DerivedReducible]: DerivedReducible[[x] =>> F[G[x]]] = - new Delegated(Reducible[F].compose[G]) + given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedReducible[[x] =>> F[G[x]]] with + private val underlying = F.unify `compose` G.unify + export underlying._ + + inline given derived[F[_]]: DerivedReducible[F] = summonFrom { + case gen: K1.ProductGeneric[F] => + given K1.ProductGeneric[F] = gen + product(K1.summonFirst[Of, gen.MirroredElemTypes, Const[Any]]) + case given K1.CoproductInstances[Of, F] => + coproduct + } - def product[F[_]](ev: Reducible[?])(using K1.ProductInstances[DerivedFoldable, F]): DerivedReducible[F] = - new Product[DerivedFoldable, F](ev) {} + def product[F[_]](ev: Of[Const[Any]])(using K1.ProductInstances[Of, F]): DerivedReducible[F] = + new Product[Of, F](ev) {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedReducible, F]): DerivedReducible[F] = - new Coproduct[DerivedReducible, F] {} + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedReducible[F] = + new Coproduct[Of, F] {} - trait Product[T[x[_]] <: Foldable[x], F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[T, F]) + trait Product[T[x[_]] <: Of[x], F[_]](ev: Of[Const[Any]])(using inst: K1.ProductInstances[T, F]) extends DerivedFoldable.Product[T, F], DerivedReducible[F]: private val evalNone = Eval.now(None) @@ -29,44 +37,32 @@ object DerivedReducible extends DerivedReducibleLowPriority: 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.foldLeft(fa, b)(g))) - case None => Continue(tf.reduceLeftToOption(fa)(f)(g)) + 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.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.foldRight(fa, Eval.now(b))(g).map(Some.apply) - case None => tf.reduceRightToOption(fa)(f)(g) + case Some(b) => tf.unify.foldRight(fa, Eval.now(b))(g).map(Some.apply) + case None => tf.unify.reduceRightToOption(fa)(f)(g) }) ).map(_.get) - trait Coproduct[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F]) + 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.reduceLeftTo(fa)(f)(g) + 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.reduceRightTo(fa)(f)(g)) + Eval.defer(tf.unify.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 866666ba..6056da0e 100644 --- a/core/src/main/scala-3/cats/derived/traverse.scala +++ b/core/src/main/scala-3/cats/derived/traverse.scala @@ -2,31 +2,36 @@ package cats.derived import cats.{Applicative, Eval, Traverse} import shapeless3.deriving.{Const, Continue, K1} -import scala.annotation.threadUnsafe +import scala.compiletime.* object traverse extends TraverseDerivation, Instances -trait DerivedTraverse[F[_]] extends Traverse[F] -object DerivedTraverse extends DerivedTraverseLowPriority: +trait DerivedTraverse[F[_]] extends DerivedFunctor[F], DerivedFoldable[F], Traverse[F] +object DerivedTraverse: + type Of[F[_]] = Alt1[Traverse, DerivedTraverse, F] + 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) - given delegated[F[_]](using F: => Traverse[F]): DerivedTraverse[F] = - new Delegated(F) + given composed[F[_], G[_]](using F: Of[F], G: Of[G]): DerivedTraverse[[x] =>> F[G[x]]] with + private val underlying = F.unify `compose` G.unify + export underlying._ - given composed[F[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] = - new Delegated(Traverse[F].compose[G]) + inline given derived[F[_]]: DerivedTraverse[F] = summonFrom { + case given K1.ProductInstances[Of, F] => product + case given K1.CoproductInstances[Of, F] => coproduct + } - def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] = - new Product[DerivedTraverse, F] {} + def product[F[_]](using K1.ProductInstances[Of, F]): DerivedTraverse[F] = + new Product[Of, F] {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedTraverse, F]): DerivedTraverse[F] = - new Coproduct[DerivedTraverse, F] {} + def coproduct[F[_]](using K1.CoproductInstances[Of, F]): DerivedTraverse[F] = + new Coproduct[Of, F] {} - trait Product[T[x[_]] <: Traverse[x], F[_]](using inst: K1.ProductInstances[T, F]) + 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]] = @@ -37,10 +42,10 @@ object DerivedTraverse extends DerivedTraverseLowPriority: } { [a, b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga) } { [f[_]] => (tf: T[f], fa: f[A]) => - tf.traverse(fa)(f) + tf.unify.traverse(fa)(f) } - trait Coproduct[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, 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]] = @@ -51,17 +56,9 @@ object DerivedTraverse extends DerivedTraverseLowPriority: } { [a, b] => (gf: G[a => b], ga: G[a]) => G.ap(gf)(ga) } { [f[_]] => (tf: T[f], fa: f[A]) => - tf.traverse(fa)(f) + tf.unify.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/EmptyTests.scala b/core/src/test/scala-3/cats/derived/EmptyTests.scala index 3390e681..9e6e9b10 100644 --- a/core/src/test/scala-3/cats/derived/EmptyTests.scala +++ b/core/src/test/scala-3/cats/derived/EmptyTests.scala @@ -4,8 +4,11 @@ import alleycats._ import cats._ import cats.derived.all._ -class EmptyTests: +object EmptyTests: case class Foo(i: Int, b: IntTree) derives Empty enum IntTree: case Leaf case Node(left: IntTree, value: Int, right: IntTree) + + @main def test() = + println(Empty[Foo].empty) diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index d327918e..4717ec87 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -4,7 +4,7 @@ import cats.Reducible import cats.data.NonEmptyList import cats.derived.all._ -class ReducibleTests { +object ReducibleTests { case class Box[A](value: A) derives Reducible