diff --git a/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala b/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala index 803538a..31cfaff 100644 --- a/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala +++ b/modules/deriving/src/main/scala/shapeless3/deriving/kinds.scala @@ -22,20 +22,22 @@ import scala.Tuple.Union import scala.compiletime.* import scala.compiletime.ops.int.S import scala.deriving.* -import scala.util.NotGiven object K0: - type Kind[C, O] = C { - type Kind = K0.type + infix type of[M <: Mirror, O] = M { type MirroredType = O type MirroredMonoType = O type MirroredElemTypes <: Tuple } + type Kind[M <: Mirror, O] = (M of O) { type Kind = K0.type } type Generic[O] = Kind[Mirror, O] type ProductGeneric[O] = Kind[Mirror.Product, O] type CoproductGeneric[O] = Kind[Mirror.Sum, O] + object Generic: + given fromMirror[M <: Mirror, O](using m: M of O): Kind[m.type, O] = m.asInstanceOf + def Generic[O](using gen: Generic[O]): gen.type = gen def ProductGeneric[O](using gen: ProductGeneric[O]): gen.type = gen def CoproductGeneric[O](using gen: CoproductGeneric[O]): gen.type = gen @@ -166,29 +168,32 @@ object K0: inline def widen[G[t] >: F[t]]: CoproductInstances[G, T] = inst.asInstanceOf - inline given mkInstances[F[_], T](using gen: Generic[T]): Instances[F, T] = + inline given mkInstances[F[_], T](using gen: Mirror of T): Instances[F, T] = inline gen match - case p: ProductGeneric[T] => mkProductInstances[F, T](using p) - case c: CoproductGeneric[T] => mkCoproductInstances[F, T](using c) + case given (Mirror.Product of T) => mkProductInstances[F, T] + case given (Mirror.Sum of T) => mkCoproductInstances[F, T] - inline given mkProductInstances[F[_], T](using gen: ProductGeneric[T]): ProductInstances[F, T] = + inline given mkProductInstances[F[_], T](using gen: Mirror.Product of T): ProductInstances[F, T] = ErasedProductInstances[K0.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) - inline given mkCoproductInstances[F[_], T](using gen: CoproductGeneric[T]): CoproductInstances[F, T] = + inline given mkCoproductInstances[F[_], T](using gen: Mirror.Sum of T): CoproductInstances[F, T] = ErasedCoproductInstances[K0.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen): CoproductInstances[F, T] object K1: - type Kind[C, O[_]] = C { - type Kind = K1.type + infix type of[M <: Mirror, O[_]] = M { type MirroredType[X] = O[X] type MirroredMonoType = O[Any] type MirroredElemTypes[_] <: Tuple } + type Kind[M <: Mirror, O[_]] = (M of O) { type Kind = K1.type } type Generic[O[_]] = Kind[Mirror, O] type ProductGeneric[O[_]] = Kind[Mirror.Product, O] type CoproductGeneric[O[_]] = Kind[Mirror.Sum, O] + object Generic: + given fromMirror[M <: Mirror, O[_]](using m: M of O): Kind[m.type, O] = m.asInstanceOf + def Generic[O[_]](using gen: Generic[O]): gen.type = gen def ProductGeneric[O[_]](using gen: ProductGeneric[O]): gen.type = gen def CoproductGeneric[O[_]](using gen: CoproductGeneric[O]): gen.type = gen @@ -315,29 +320,32 @@ object K1: inline def widen[G[t[_]] >: F[t]]: CoproductInstances[G, T] = inst.asInstanceOf - inline given mkInstances[F[_[_]], T[_]](using gen: Generic[T]): Instances[F, T] = + inline given mkInstances[F[_[_]], T[_]](using gen: Mirror of T): Instances[F, T] = inline gen match - case p: ProductGeneric[T] => mkProductInstances[F, T](using p) - case c: CoproductGeneric[T] => mkCoproductInstances[F, T](using c) + case given (Mirror.Product of T) => mkProductInstances[F, T] + case given (Mirror.Sum of T) => mkCoproductInstances[F, T] - inline given mkProductInstances[F[_[_]], T[_]](using gen: ProductGeneric[T]): ProductInstances[F, T] = + inline given mkProductInstances[F[_[_]], T[_]](using gen: Mirror.Product of T): ProductInstances[F, T] = ErasedProductInstances[K1.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) - inline given mkCoproductInstances[F[_[_]], T[_]](using gen: CoproductGeneric[T]): CoproductInstances[F, T] = + inline given mkCoproductInstances[F[_[_]], T[_]](using gen: Mirror.Sum of T): CoproductInstances[F, T] = ErasedCoproductInstances[K1.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) object K11: - type Kind[C, O[_[_]]] = C { - type Kind = K11.type + infix type of[M <: Mirror, O[_[_]]] = M { type MirroredType[X[_]] = O[X] type MirroredMonoType = O[[_] =>> Any] type MirroredElemTypes[_[_]] <: Tuple } + type Kind[M <: Mirror, O[_[_]]] = (M of O) { type Kind = K11.type } type Generic[O[_[_]]] = Kind[Mirror, O] type ProductGeneric[O[_[_]]] = Kind[Mirror.Product, O] type CoproductGeneric[O[_[_]]] = Kind[Mirror.Sum, O] + object Generic: + given fromMirror[M <: Mirror, O[_[_]]](using m: M of O): Kind[m.type, O] = m.asInstanceOf + def Generic[O[_[_]]](using gen: Generic[O]): gen.type = gen def ProductGeneric[O[_[_]]](using gen: ProductGeneric[O]): gen.type = gen def CoproductGeneric[O[_[_]]](using gen: CoproductGeneric[O]): gen.type = gen @@ -471,29 +479,32 @@ object K11: inline def widen[G[t[_[_]]] >: F[t]]: CoproductInstances[G, T] = inst.asInstanceOf - inline given mkInstances[F[_[_[_]]], T[_[_]]](using gen: Generic[T]): Instances[F, T] = + inline given mkInstances[F[_[_[_]]], T[_[_]]](using gen: Mirror of T): Instances[F, T] = inline gen match - case p: ProductGeneric[T] => mkProductInstances[F, T](using p) - case c: CoproductGeneric[T] => mkCoproductInstances[F, T](using c) + case given (Mirror.Product of T) => mkProductInstances[F, T] + case given (Mirror.Sum of T) => mkCoproductInstances[F, T] - inline given mkProductInstances[F[_[_[_]]], T[_[_]]](using gen: ProductGeneric[T]): ProductInstances[F, T] = + inline given mkProductInstances[F[_[_[_]]], T[_[_]]](using gen: Mirror.Product of T): ProductInstances[F, T] = ErasedProductInstances[K11.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) - inline given mkCoproductInstances[F[_[_[_]]], T[_[_]]](using gen: CoproductGeneric[T]): CoproductInstances[F, T] = + inline given mkCoproductInstances[F[_[_[_]]], T[_[_]]](using gen: Mirror.Sum of T): CoproductInstances[F, T] = ErasedCoproductInstances[K11.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) object K2: - type Kind[C, O[_, _]] = C { - type Kind = K2.type + infix type of[M <: Mirror, O[_, _]] = M { type MirroredType[X, Y] = O[X, Y] type MirroredMonoType = O[Any, Any] type MirroredElemTypes[_, _] <: Tuple } + type Kind[M <: Mirror, O[_, _]] = (M of O) { type Kind = K2.type } type Generic[O[_, _]] = Kind[Mirror, O] type ProductGeneric[O[_, _]] = Kind[Mirror.Product, O] type CoproductGeneric[O[_, _]] = Kind[Mirror.Sum, O] + object Generic: + given fromMirror[M <: Mirror, O[_, _]](using m: M of O): Kind[m.type, O] = m.asInstanceOf + def Generic[O[_, _]](using gen: Generic[O]): gen.type = gen def ProductGeneric[O[_, _]](using gen: ProductGeneric[O]): gen.type = gen def CoproductGeneric[O[_, _]](using gen: CoproductGeneric[O]): gen.type = gen @@ -632,13 +643,13 @@ object K2: inline def widen[G[t[_, _]] >: F[t]]: CoproductInstances[G, T] = inst.asInstanceOf - inline given mkInstances[F[_[_, _]], T[_, _]](using gen: Generic[T]): Instances[F, T] = + inline given mkInstances[F[_[_, _]], T[_, _]](using gen: Mirror of T): Instances[F, T] = inline gen match - case p: ProductGeneric[T] => mkProductInstances[F, T](using p) - case c: CoproductGeneric[T] => mkCoproductInstances[F, T](using c) + case given (Mirror.Product of T) => mkProductInstances[F, T] + case given (Mirror.Sum of T) => mkCoproductInstances[F, T] - inline given mkProductInstances[F[_[_, _]], T[_, _]](using gen: ProductGeneric[T]): ProductInstances[F, T] = + inline given mkProductInstances[F[_[_, _]], T[_, _]](using gen: Mirror.Product of T): ProductInstances[F, T] = ErasedProductInstances[K2.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) - inline given mkCoproductInstances[F[_[_, _]], T[_, _]](using gen: CoproductGeneric[T]): CoproductInstances[F, T] = + inline given mkCoproductInstances[F[_[_, _]], T[_, _]](using gen: Mirror.Sum of T): CoproductInstances[F, T] = ErasedCoproductInstances[K2.type, F[T], LiftP[F, gen.MirroredElemTypes]](gen) diff --git a/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala b/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala index efe0044..09dc54e 100644 --- a/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala +++ b/modules/deriving/src/test/scala/shapeless3/deriving/deriving.scala @@ -16,16 +16,16 @@ package shapeless3.deriving +import cats.Eval import org.junit.Assert.* import org.junit.Test +import shapeless3.deriving.adts.* +import shapeless3.deriving.adts.OptE.{NnE, SmE} import scala.annotation.tailrec -import scala.compiletime.constValueTuple - -import cats.Eval - -import adts.* -import OptE.{SmE, NnE} +import scala.compiletime.{constValueTuple, testing} +import scala.compiletime.testing.typeCheckErrors +import scala.deriving.Mirror // Tests @@ -393,3 +393,52 @@ class DerivationTests: assertEquals(Right(ISB(42, "foo", true)), parser.parseShort("s=foo,i=42,b=true,hidden=?")) assertEquals(Left("Missing field 's';"), parser.parseShort("i=42,b=kinda")) assertEquals(Left("Invalid field 'broken';"), parser.parseShort("i=42,broken,?")) + + @Test + def userDefinedMirrors(): Unit = + def assertImportSuggested(errors: List[testing.Error]): Unit = + assertEquals(1, errors.size) + val message = errors.head.message.trim + assert(message.startsWith("No given instance of type")) + assert(message.endsWith("Generic.fromMirror")) + + def show[A](using + m: Mirror.ProductOf[A] { + type MirroredLabel = "ISB" + type MirroredElemLabels = ("i", "s", "b") + type MirroredElemTypes = (Int, String, Boolean) + } + ): Unit = + assertImportSuggested(typeCheckErrors("K0.Generic[A]")) + import K0.Generic.fromMirror + assertEquals(m, K0.Generic[A]) + assertNotNull(Show[A]) + + def bifunctor[F[_, _]](using + m: Mirror.Sum { + type MirroredType[A, B] = F[A, B] + type MirroredMonoType = F[Any, Any] + type MirroredElemTypes[A, B] = ((A, B), Unit) + } + ): Unit = + assertImportSuggested(typeCheckErrors("K2.Generic[F]")) + import K2.Generic.fromMirror + assertEquals(m, K2.CoproductGeneric[F]) + assertNotNull(Bifunctor[F]) + + def functorK[Alg[_[_]]](using + m: Mirror.Product { + type MirroredType[F[_]] = Alg[F] + type MirroredMonoType = Alg[[_] =>> Any] + type MirroredElemTypes[F[_]] = (F[String], F[Int]) + } + ): Unit = + assertImportSuggested(typeCheckErrors("K11.Generic[Alg]")) + import K11.Generic.fromMirror + assertEquals(m, K11.Generic[Alg]) + assertNotNull(FunctorK[Alg]) + + show[ISB] + bifunctor[ListF] + functorK[Order] + end userDefinedMirrors