diff --git a/core/src/main/scala-3/cats/derived/derived.scala b/core/src/main/scala-3/cats/derived/derived.scala new file mode 100644 index 00000000..6c6c0ddc --- /dev/null +++ b/core/src/main/scala-3/cats/derived/derived.scala @@ -0,0 +1,35 @@ +package cats.derived + +import shapeless3.deriving.* +import scala.annotation.* +import scala.compiletime.* + +opaque type Derived[A] = A +object Derived: + def apply[A](instance: A): Derived[A] = instance + extension [A](derived: Derived[A]) def instance: A = derived + given [A]: Conversion[A, Derived[A]] = apply + + type Or0[F[_]] = [x] =>> Or[F[x]] + type Or1[F[_[_]]] = [x[_]] =>> Or[F[x]] + type Or11[F[_[_[_]]]] = [x[_[_]]] =>> Or[F[x]] + type Or2[F[_[_, _]]] = [x[_, _]] =>> Or[F[x]] + + opaque type Or[A] = A + object Or extends OrInstances: + def apply[A](instance: A): Or[A] = instance + extension [A](or: Or[A]) def unify: A = or + extension [I[f[_], t] <: K0.Instances[f, t], F[_], T] + (inst: I[Or0[F], T]) @targetName("unifyK0") def unify: I[F, T] = inst + extension [I[f[_[_]], t[_]] <: K1.Instances[f, t], F[_[_]], T[_]] + (inst: I[Or1[F], T]) @targetName("unifyK1") def unify: I[F, T] = inst + extension [I[f[_[_[_]]], t[_[_]]] <: K11.Instances[f, t], F[_[_[_]]], T[_[_]]] + (inst: I[Or11[F], T]) @targetName("unifyK11") def unify: I[F, T] = inst + extension [I[f[_[_, _]], t[_, _]] <: K2.Instances[f, t], F[_[_, _]], T[_, _]] + (inst: I[Or2[F], T]) @targetName("unifyK2") def unify: I[F, T] = inst + +sealed abstract class OrInstances: + inline given [A]: Derived.Or[A] = summonFrom { + case instance: A => Derived.Or(instance) + case derived: Derived[A] => Derived.Or(derived.instance) + } diff --git a/core/src/main/scala-3/cats/derived/empty.scala b/core/src/main/scala-3/cats/derived/empty.scala index 0260d426..1b650c58 100644 --- a/core/src/main/scala-3/cats/derived/empty.scala +++ b/core/src/main/scala-3/cats/derived/empty.scala @@ -2,30 +2,23 @@ package cats.derived import alleycats.Empty import shapeless3.deriving.K0 -import scala.compiletime.summonFrom +import scala.compiletime.* object empty extends EmptyDerivation -trait DerivedEmpty[A] extends Empty[A]: - protected def emptyValue(): A - lazy val empty: A = emptyValue() - +type DerivedEmpty[A] = Derived[Empty[A]] object DerivedEmpty: - inline given [A]: DerivedEmpty[A] = summonFrom { - case given Empty[A] => delegated - case given K0.ProductInstances[DerivedEmpty, A] => product - case given K0.CoproductGeneric[A] => coproduct - } - - def delegated[A](using A: => Empty[A]): DerivedEmpty[A] = - () => A.empty + type Or[A] = Derived.Or[Empty[A]] + inline def apply[A]: Empty[A] = + import DerivedEmpty.given + summonInline[DerivedEmpty[A]].instance - def product[A](using inst: K0.ProductInstances[DerivedEmpty, A]): DerivedEmpty[A] = - () => inst.construct([A] => (A: DerivedEmpty[A]) => A.empty) + given product[A](using inst: K0.ProductInstances[Or, A]): DerivedEmpty[A] = + Empty(inst.unify.construct([A] => (A: Empty[A]) => A.empty)) - inline def coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] = - K0.summonFirst[DerivedEmpty, gen.MirroredElemTypes, A] + inline given coproduct[A](using gen: K0.CoproductGeneric[A]): DerivedEmpty[A] = + K0.summonFirst[Or, gen.MirroredElemTypes, A].unify trait EmptyDerivation: extension (E: Empty.type) - def derived[A](using instance: DerivedEmpty[A]): Empty[A] = instance + inline def derived[A]: Empty[A] = DerivedEmpty[A] diff --git a/core/src/main/scala-3/cats/derived/foldable.scala b/core/src/main/scala-3/cats/derived/foldable.scala index 2f4be05f..f5121bf1 100644 --- a/core/src/main/scala-3/cats/derived/foldable.scala +++ b/core/src/main/scala-3/cats/derived/foldable.scala @@ -2,30 +2,32 @@ 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: - given const[T]: DerivedFoldable[Const[T]] with +type DerivedFoldable[F[_]] = Derived[Foldable[F]] +object DerivedFoldable: + type Or[F[_]] = Derived.Or[Foldable[F]] + inline def apply[F[_]]: Foldable[F] = + import DerivedFoldable.given + summonInline[DerivedFoldable[F]].instance + + given [T]: DerivedFoldable[Const[T]] = new Foldable[Const[T]]: 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[_]: DerivedFoldable, G[_]: DerivedFoldable]: DerivedFoldable[[x] =>> F[G[x]]] = - new Delegated(Foldable[F].compose[G]) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFoldable[[x] =>> F[G[x]]] = + F.unify `compose` G.unify - def product[F[_]](using K1.ProductInstances[DerivedFoldable, F]): DerivedFoldable[F] = - new Product[DerivedFoldable, F] {} + given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedFoldable[F] = + new Product(using inst.unify) {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedFoldable, F]): DerivedFoldable[F] = - new Coproduct[DerivedFoldable, F] {} + given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedFoldable[F] = + new Coproduct(using inst.unify) {} trait Product[T[x[_]] <: Foldable[x], F[_]](using inst: K1.ProductInstances[T, F]) - extends DerivedFoldable[F]: + extends Foldable[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]) => @@ -38,7 +40,7 @@ object DerivedFoldable extends DerivedFoldableLowPriority: } trait Coproduct[T[x[_]] <: Foldable[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends DerivedFoldable[F]: + extends Foldable[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]) => @@ -50,14 +52,6 @@ object DerivedFoldable extends DerivedFoldableLowPriority: 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 + inline def derived[F[_]]: Foldable[F] = DerivedFoldable[F] diff --git a/core/src/main/scala-3/cats/derived/functor.scala b/core/src/main/scala-3/cats/derived/functor.scala index b87f0051..aa9256c1 100644 --- a/core/src/main/scala-3/cats/derived/functor.scala +++ b/core/src/main/scala-3/cats/derived/functor.scala @@ -2,40 +2,34 @@ 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: - given const[T]: DerivedFunctor[Const[T]] with - def map[A, B](fa: T)(f: A => B): T = fa +type DerivedFunctor[F[_]] = Derived[Functor[F]] +object DerivedFunctor: + type Or[F[_]] = Derived.Or[Functor[F]] + inline def apply[F[_]]: Functor[F] = + import DerivedFunctor.given + summonInline[DerivedFunctor[F]].instance - given delegated[F[_]](using F: => Functor[F]): DerivedFunctor[F] = - new Delegated(F) + given [T]: DerivedFunctor[Const[T]] = new Functor[Const[T]]: + def map[A, B](fa: T)(f: A => B): T = fa - given composed[F[_]: DerivedFunctor, G[_]: DerivedFunctor]: DerivedFunctor[[x] =>> F[G[x]]] = - new Delegated(Functor[F].compose[G]) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedFunctor[[x] =>> F[G[x]]] = + F.unify `compose` G.unify - def generic[F[_]](using K1.Instances[DerivedFunctor, F]): DerivedFunctor[F] = - new Generic[DerivedFunctor, F] {} + given [F[_]](using inst: => K1.Instances[Or, F]): DerivedFunctor[F] = + new Generic(using inst.unify) {} trait Generic[T[x[_]] <: Functor[x], F[_]](using inst: K1.Instances[T, F]) - extends DerivedFunctor[F]: + extends Functor[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) } - 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 + inline def derived[F[_]]: Functor[F] = DerivedFunctor[F] diff --git a/core/src/main/scala-3/cats/derived/reducible.scala b/core/src/main/scala-3/cats/derived/reducible.scala index 83f47c57..36541219 100644 --- a/core/src/main/scala-3/cats/derived/reducible.scala +++ b/core/src/main/scala-3/cats/derived/reducible.scala @@ -2,26 +2,34 @@ 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) +type DerivedReducible[F[_]] = Derived[Reducible[F]] +object DerivedReducible: + type Or[F[_]] = Derived.Or[Reducible[F]] + inline def apply[F[_]]: Reducible[F] = + import DerivedFoldable.given + import DerivedReducible.given + summonInline[DerivedReducible[F]].instance - given composed[F[_]: DerivedReducible, G[_]: DerivedReducible]: DerivedReducible[[x] =>> F[G[x]]] = - new Delegated(Reducible[F].compose[G]) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedReducible[[x] =>> F[G[x]]] = + F.unify `compose` G.unify - def product[F[_]](ev: Reducible[?])(using K1.ProductInstances[DerivedFoldable, F]): DerivedReducible[F] = - new Product[DerivedFoldable, F](ev) {} + def product[F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[DerivedFoldable.Or, F]): DerivedReducible[F] = + given K1.ProductInstances[Foldable, F] = inst.unify + new Product[Foldable, F](ev) {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedReducible, F]): DerivedReducible[F] = - new Coproduct[DerivedReducible, F] {} + inline given [F[_]](using gen: K1.ProductGeneric[F]): DerivedReducible[F] = + product(K1.summonFirst[Or, gen.MirroredElemTypes, Const[Any]].unify) + + given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedReducible[F] = + given K1.CoproductInstances[Reducible, F] = inst.unify + new Coproduct[Reducible, F] {} trait Product[T[x[_]] <: Foldable[x], F[_]](ev: Reducible[?])(using inst: K1.ProductInstances[T, F]) - extends DerivedFoldable.Product[T, F], DerivedReducible[F]: + extends DerivedFoldable.Product[T, F], Reducible[F]: private val evalNone = Eval.now(None) @@ -43,7 +51,7 @@ object DerivedReducible extends DerivedReducibleLowPriority: ).map(_.get) trait Coproduct[T[x[_]] <: Reducible[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends DerivedFoldable.Coproduct[T, F], DerivedReducible[F]: + extends DerivedFoldable.Coproduct[T, F], Reducible[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]) => @@ -55,18 +63,6 @@ object DerivedReducible extends DerivedReducibleLowPriority: 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 + inline def derived[F[_]]: Reducible[F] = DerivedReducible[F] diff --git a/core/src/main/scala-3/cats/derived/traverse.scala b/core/src/main/scala-3/cats/derived/traverse.scala index 866666ba..f6e0c066 100644 --- a/core/src/main/scala-3/cats/derived/traverse.scala +++ b/core/src/main/scala-3/cats/derived/traverse.scala @@ -2,32 +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: - given const[T]: DerivedTraverse[Const[T]] with +type DerivedTraverse[F[_]] = Derived[Traverse[F]] +object DerivedTraverse: + type Or[F[_]] = Derived.Or[Traverse[F]] + inline def apply[F[_]]: Traverse[F] = + import DerivedTraverse.given + summonInline[DerivedTraverse[F]].instance + + given [T]: DerivedTraverse[Const[T]] = new Traverse[Const[T]]: 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[_]: DerivedTraverse, G[_]: DerivedTraverse]: DerivedTraverse[[x] =>> F[G[x]]] = - new Delegated(Traverse[F].compose[G]) + given [F[_], G[_]](using F: Or[F], G: Or[G]): DerivedTraverse[[x] =>> F[G[x]]] = + F.unify `compose` G.unify - def product[F[_]](using K1.ProductInstances[DerivedTraverse, F]): DerivedTraverse[F] = - new Product[DerivedTraverse, F] {} + given [F[_]](using inst: K1.ProductInstances[Or, F]): DerivedTraverse[F] = + given K1.ProductInstances[Traverse, F] = inst.unify + new Product[Traverse, F] {} - def coproduct[F[_]](using K1.CoproductInstances[DerivedTraverse, F]): DerivedTraverse[F] = - new Coproduct[DerivedTraverse, F] {} + given [F[_]](using inst: => K1.CoproductInstances[Or, F]): DerivedTraverse[F] = + given K1.CoproductInstances[Traverse, F] = inst.unify + new Coproduct[Traverse, 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]: + extends DerivedFunctor.Generic[T, F], DerivedFoldable.Product[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) => @@ -41,7 +45,7 @@ object DerivedTraverse extends DerivedTraverseLowPriority: } trait Coproduct[T[x[_]] <: Traverse[x], F[_]](using inst: K1.CoproductInstances[T, F]) - extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[T, F], DerivedTraverse[F]: + extends DerivedFunctor.Generic[T, F], DerivedFoldable.Coproduct[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) => @@ -54,14 +58,6 @@ object DerivedTraverse extends DerivedTraverseLowPriority: 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 + inline def derived[F[_]]: Traverse[F] = DerivedTraverse[F] diff --git a/core/src/test/scala-3/cats/derived/EmptyTests.scala b/core/src/test/scala-3/cats/derived/EmptyTests.scala index 3390e681..b6823d25 100644 --- a/core/src/test/scala-3/cats/derived/EmptyTests.scala +++ b/core/src/test/scala-3/cats/derived/EmptyTests.scala @@ -4,7 +4,7 @@ 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 diff --git a/core/src/test/scala-3/cats/derived/ReducibleTests.scala b/core/src/test/scala-3/cats/derived/ReducibleTests.scala index d327918e..6eb26f80 100644 --- a/core/src/test/scala-3/cats/derived/ReducibleTests.scala +++ b/core/src/test/scala-3/cats/derived/ReducibleTests.scala @@ -2,10 +2,9 @@ package cats.derived import cats.Reducible import cats.data.NonEmptyList -import cats.derived.all._ - -class ReducibleTests { +import cats.derived.all.* +object ReducibleTests: case class Box[A](value: A) derives Reducible sealed trait OneOrMany[+A] derives Reducible @@ -26,6 +25,10 @@ class ReducibleTests { case Non case Con(v: A, r: MyList[A]) - summon[DerivedFunctor[MyList]] - summon[DerivedReducible[NonEmptyTree]] -} + import cats._ + + DerivedFunctor[MyList] + DerivedReducible[NonEmptyTree] + +@main def run() = + println(DerivedFunctor[ReducibleTests.MyList].map(ReducibleTests.MyList.Con(42, ReducibleTests.MyList.Non))(_.toString))