Skip to content

Commit

Permalink
Added K11 kind to deriving
Browse files Browse the repository at this point in the history
  • Loading branch information
Kalin-Rudnicki committed Apr 1, 2024
1 parent 23b3bac commit d7dba77
Show file tree
Hide file tree
Showing 6 changed files with 304 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@ abstract class K0T[UB] {
inline def summonFieldInstances[T <: Tuple, F[_], G[_ <: UB]]: List[F[G[UB]]] =
summonAll[FieldInstances[T, F, G]].toIArray.toList.asInstanceOf[List[F[G[UB]]]]

final case class ProductInstances[F <: UB, T[_ <: UB]](
m: Mirror.ProductOf[F],
ev: F <:< Product,
rawInstances: List[LazyDerived[T[UB]]],
final class ProductInstances[F <: UB, T[_ <: UB]](val m: ProductGeneric[F])(
val ev: m.MirroredMonoType <:< Product,
val rawInstances: List[LazyDerived[T[UB]]],
) {

lazy val instances: List[T[UB]] = rawInstances.map(_.derived)

def instantiate(fields: List[m.MirroredType]): F =
m.asInstanceOf[Mirror.ProductOf[m.MirroredMonoType]].fromTuple(Tuple.fromArray(fields.toArray[Any]).asInstanceOf)

final case class withInstance(a: F) {

private def productElements: List[UB] = ev(a).productIterator.toList.asInstanceOf[List[UB]]
Expand Down Expand Up @@ -76,24 +78,20 @@ abstract class K0T[UB] {
}
object ProductInstances {
inline given of[F <: UB, T[_ <: UB]](using m: ProductGeneric[F]): ProductInstances[F, T] =
ProductInstances[F, T](
m,
summonInline[F <:< Product],
// summonAll[FieldInstances[m.MirroredElemTypes, LazyDerived, F]].toIArray.toList.asInstanceOf[List[LazyDerived[F[UB]]]],
new ProductInstances[F, T](m)(
summonInline[m.MirroredMonoType <:< Product],
summonFieldInstances[m.MirroredElemTypes, LazyDerived, T],
)
}

final case class SumInstances[F <: UB, T[_ <: UB]](
m: Mirror.SumOf[F],
children: List[T[UB]],
final class SumInstances[F <: UB, T[_ <: UB]](val m: SumGeneric[F])(
val children: List[T[UB]],
) {

def narrow[G[B <: UB] <: T[B]](implicit fCt: ClassTag[T[UB]], gCt: ClassTag[G[UB]]): SumInstances[F, G] =
SumInstances[F, G](
m,
def narrow[G[B <: UB] <: T[B]](implicit fCt: ClassTag[T[m.MirroredType]], gCt: ClassTag[G[m.MirroredType]]): SumInstances[F, G] =
new SumInstances[F, G](m)(
children.asInstanceOf[List[Matchable]].map {
case gCt(c) => c
case gCt(c) => c.asInstanceOf
case other => throw new RuntimeException(s"Unable to narrow ${fCt.runtimeClass.getName} to ${gCt.runtimeClass.getName} ($other)")
},
)
Expand All @@ -106,8 +104,7 @@ abstract class K0T[UB] {
}
object SumInstances {
inline given of[A <: UB, F[_ <: UB]](using m: SumGeneric[A]): SumInstances[A, F] =
SumInstances[A, F](
m,
new SumInstances[A, F](m)(
summonFieldInstances[m.MirroredElemTypes, Derived, F].map(_.derived),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,16 @@ abstract class K1T[UB] {
inline def summonFieldInstances[T <: [_ <: UB] =>> Tuple, F[_], G[_[_ <: UB]]]: List[F[G[[_ <: UB] =>> Any]]] =
summonAll[FieldInstances[T, F, G]].toIArray.toList.asInstanceOf[List[F[G[[_ <: UB] =>> Any]]]]

// TODO (KR) : remove
type AnyF[X <: UB] = Any

final case class ProductInstances[F[_ <: UB], T[_[_ <: UB]]](
m: ProductGeneric[F],
ev: F[UB] <:< Product,
rawInstances: List[LazyDerived[T[[_ <: UB] =>> Any]]],
final class ProductInstances[F[_ <: UB], T[_[_ <: UB]]](val m: ProductGeneric[F])(
val ev: m.MirroredMonoType <:< Product,
val rawInstances: List[LazyDerived[T[[_ <: UB] =>> Any]]],
) {

lazy val instances: List[T[[_ <: UB] =>> Any]] = rawInstances.map(_.derived)

def instantiate[A <: UB](fields: List[m.MirroredType[A]]): F[A] =
m.asInstanceOf[Mirror.ProductOf[m.MirroredMonoType]].fromTuple(Tuple.fromArray(fields.toArray[Any]).asInstanceOf).asInstanceOf[F[A]]

final case class withInstance[A <: UB](a: F[A]) {

private def productElements: List[T[[_ <: UB] =>> Any]] = ev(a.asInstanceOf).productIterator.toList.asInstanceOf[List[T[[_ <: UB] =>> Any]]]
Expand All @@ -64,12 +63,10 @@ abstract class K1T[UB] {
object mapInstantiate {

def apply[B <: UB](f: [t[_ <: UB]] => (T[t], t[A]) => t[B]): F[B] =
m.asInstanceOf[Mirror.ProductOf[F[B]]]
.fromTuple(Tuple.fromArray(map(f).toArray).asInstanceOf)
instantiate(map(f).asInstanceOf)

def withLabels[B <: UB](labels: Labelling[m.MirroredMonoType])(f: [t[_ <: UB]] => (String, T[t], t[A]) => t[B]): F[B] =
m.asInstanceOf[Mirror.ProductOf[F[B]]]
.fromTuple(Tuple.fromArray(map.withLabels(labels)(f).toArray).asInstanceOf)
instantiate(map.withLabels(labels)(f).asInstanceOf)

}

Expand All @@ -78,7 +75,7 @@ abstract class K1T[UB] {
def apply[R](z: R)(f: [t[_ <: UB]] => (R, T[t], t[A]) => R): R =
productElements.zip(instances).foldLeft(z) { case (acc, (b, i)) => f(acc, i, b) }

def withLabels[R](labels: Labelling[m.MirroredMonoType], z: R)(f: [t[_ <: UB]] => (R, String, T[t], t[UB]) => R): R =
def withLabels[R](labels: Labelling[m.MirroredMonoType], z: R)(f: [t[_ <: UB]] => (R, String, T[t], t[A]) => R): R =
labels.elemLabels.zip(productElements).zip(instances).foldLeft(z) { case (acc, ((l, b), i)) => f(acc, l, i, b) }

}
Expand All @@ -102,22 +99,19 @@ abstract class K1T[UB] {
}
object ProductInstances {
inline given of[F[_ <: UB], T[_[_ <: UB]]](using m: ProductGeneric[F]): ProductInstances[F, T] =
ProductInstances[F, T](
m,
summonInline[ProductGeneric[F]#MirroredMonoType <:< Product],
new ProductInstances[F, T](m)(
summonInline[m.MirroredMonoType <:< Product],
summonFieldInstances[m.MirroredElemTypes, LazyDerived, T],
)
}

final case class SumInstances[F[_ <: UB], T[_[_ <: UB]]](
m: SumGeneric[F],
final class SumInstances[F[_ <: UB], T[_[_ <: UB]]](val m: SumGeneric[F])(
children: List[T[[_ <: UB] =>> Any]],
) {

// TODO (KR) : Im not sure on this one...
def narrow[G[B[_ <: UB]] <: T[B]](implicit fCt: ClassTag[T[m.MirroredType]], gCt: ClassTag[G[m.MirroredType]]): SumInstances[F, G] =
SumInstances[F, G](
m,
new SumInstances[F, G](m)(
children.asInstanceOf[List[Matchable]].map {
case gCt(c) => c.asInstanceOf
case other => throw new RuntimeException(s"Unable to narrow ${fCt.runtimeClass.getName} to ${gCt.runtimeClass.getName} ($other)")
Expand All @@ -132,8 +126,7 @@ abstract class K1T[UB] {
}
object SumInstances {
inline given of[F[_ <: UB], T[_[_ <: UB]]](using m: SumGeneric[F]): SumInstances[F, T] =
SumInstances[F, T](
m,
new SumInstances[F, T](m)(
summonFieldInstances[m.MirroredElemTypes, Derived, T].map(_.derived),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package harness.deriving

import scala.compiletime.*
import scala.deriving.Mirror
import scala.reflect.ClassTag

abstract class K11T[UB] {

type Kind[C, O[_[_ <: UB]]] = C {
type MirroredType[X[_ <: UB]] = O[X]
type MirroredMonoType = O[[_ <: UB] =>> Any]
type MirroredElemTypes[_[_ <: UB]] <: Tuple
}

type Generic[O[_[_ <: UB]]] = Kind[Mirror, O]
type ProductGeneric[O[_[_ <: UB]]] = Kind[Mirror.Product, O]
type SumGeneric[O[_[_ <: UB]]] = Kind[Mirror.Sum, O]

type Id[t <: UB] = [f[_ <: UB]] =>> f[t]
type Identity[t <: UB] = t
type Const[c] = [_[_ <: UB]] =>> c
type ~>[A[_ <: UB], B[_ <: UB]] = [t <: UB] => A[t] => B[t]

type Head[T <: [_[_ <: UB]] =>> Any, A[_ <: UB]] =
T[A] match {
case h *: ? => h
}
type Tail[T <: [_[_ <: UB]] =>> Any, A[_ <: UB]] =
T[A] match {
case ? *: t => t
}

type FieldInstances[T <: [_[_ <: UB]] =>> Tuple, F[_], G[_[_[_ <: UB]]]] <: Tuple =
T[Option] match {
case ? *: ? => F[G[[X[_ <: UB]] =>> Head[T, X]]] *: FieldInstances[[X[_ <: UB]] =>> Tail[T, X], F, G]
case EmptyTuple => EmptyTuple
}

inline def summonFieldInstances[T <: [_[_ <: UB]] =>> Tuple, F[_], G[_[_[_ <: UB]]]]: List[F[G[[_[_ <: UB]] =>> Any]]] =
summonAll[FieldInstances[T, F, G]].toIArray.toList.asInstanceOf[List[F[G[[_[_ <: UB]] =>> Any]]]]

final class ProductInstances[F[_[_ <: UB]], T[_[_[_ <: UB]]]](val m: ProductGeneric[F])(
val ev: m.MirroredMonoType <:< Product,
val rawInstances: List[LazyDerived[T[[_[_ <: UB]] =>> Any]]],
) {

lazy val instances: List[T[[_[_ <: UB]] =>> Any]] = rawInstances.map(_.derived)

def instantiate[A[_ <: UB]](fields: List[m.MirroredType[A]]): F[A] =
m.asInstanceOf[Mirror.ProductOf[m.MirroredMonoType]].fromTuple(Tuple.fromArray(fields.toArray[Any]).asInstanceOf).asInstanceOf[F[A]]

final case class withInstance[A[_ <: UB]](a: F[A]) {

private def productElements: List[T[[_[_ <: UB]] =>> Any]] = ev(a.asInstanceOf).productIterator.toList.asInstanceOf[List[T[[_[_ <: UB]] =>> Any]]]

object map {

def apply[B](f: [t[_[_ <: UB]]] => (T[t], t[A]) => B): List[B] =
productElements.zip(instances).map { case (b, i) => f(i, b) }

def withLabels[B](labels: Labelling[m.MirroredMonoType])(f: [t[_[_ <: UB]]] => (String, T[t], t[A]) => B): List[B] =
labels.elemLabels.zip(productElements).zip(instances).map { case ((l, b), i) => f(l, i, b) }

}

object mapInstantiate {

def apply[B[_ <: UB]](f: [t[_[_ <: UB]]] => (T[t], t[A]) => t[B]): F[B] =
instantiate(map(f).asInstanceOf)

def withLabels[B[_ <: UB]](labels: Labelling[m.MirroredMonoType])(f: [t[_[_ <: UB]]] => (String, T[t], t[A]) => t[B]): F[B] =
instantiate(map.withLabels(labels)(f).asInstanceOf)

}

object foldLeft {

def apply[R](z: R)(f: [t[_[_ <: UB]]] => (R, T[t], t[A]) => R): R =
productElements.zip(instances).foldLeft(z) { case (acc, (b, i)) => f(acc, i, b) }

def withLabels[R](labels: Labelling[m.MirroredMonoType], z: R)(f: [t[_[_ <: UB]]] => (R, String, T[t], t[A]) => R): R =
labels.elemLabels.zip(productElements).zip(instances).foldLeft(z) { case (acc, ((l, b), i)) => f(acc, l, i, b) }

}

}

object withoutInstance {

object foldLeft {

def apply[R](z: R)(f: [t[_[_ <: UB]]] => (R, T[t]) => R): R =
instances.foldLeft(z) { case (acc, i) => f(acc, i) }

def withLabels[R](labels: Labelling[m.MirroredMonoType], z: R)(f: [t[_[_ <: UB]]] => (R, String, T[t]) => R): R =
labels.elemLabels.zip(instances).foldLeft(z) { case (acc, (l, i)) => f(acc, l, i) }

}

}

}
object ProductInstances {
inline given of[F[_[_ <: UB]], T[_[_[_ <: UB]]]](using m: ProductGeneric[F]): ProductInstances[F, T] =
new ProductInstances[F, T](m)(
summonInline[m.MirroredMonoType <:< Product],
summonFieldInstances[m.MirroredElemTypes, LazyDerived, T],
)
}

final class SumInstances[F[_[_ <: UB]], T[_[_[_ <: UB]]]](val m: SumGeneric[F])(
children: List[T[[_[_ <: UB]] =>> Any]],
) {

// TODO (KR) : Im not sure on this one...
def narrow[G[B[_[_ <: UB]]] <: T[B]](implicit fCt: ClassTag[T[m.MirroredType]], gCt: ClassTag[G[m.MirroredType]]): SumInstances[F, G] =
new SumInstances[F, G](m)(
children.asInstanceOf[List[Matchable]].map {
case gCt(c) => c.asInstanceOf
case other => throw new RuntimeException(s"Unable to narrow ${fCt.runtimeClass.getName} to ${gCt.runtimeClass.getName} ($other)")
},
)

final case class withInstance[A[_ <: UB]](a: F[A]) {
val ord: Int = m.ordinal(a.asInstanceOf)
val inst: T[F] = children(ord).asInstanceOf
}

}
object SumInstances {
inline given of[F[_[_ <: UB]], T[_[_[_ <: UB]]]](using m: SumGeneric[F]): SumInstances[F, T] =
new SumInstances[F, T](m)(
summonFieldInstances[m.MirroredElemTypes, Derived, T].map(_.derived),
)
}

trait Derivable[T[_[_[_ <: UB]]]] {

inline implicit def genProduct[F[_[_ <: UB]]](implicit m: ProductGeneric[F]): Derived[T[F]]

inline implicit def genSum[F[_[_ <: UB]]](implicit m: SumGeneric[F]): Derived[T[F]]

inline final def derive[F[_[_ <: UB]]](using m: Generic[F]): T[F] =
inline m match {
case m: ProductGeneric[F] => genProduct[F](using m).derived
case m: SumGeneric[F] => genSum[F](using m).derived
}

}

}

object K11 extends K11T[Any]
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package harness.deriving

import cats.syntax.option.*
import harness.deriving.ExampleK11Types.*
import harness.zio.*
import harness.zio.test.*
import zio.test.*

object ExampleK11DerivationSpec extends DefaultHarnessSpec {

import K11.~>

trait FunctorK[F[_[_]]] {
def mapK[A[_], B[_]](a: F[A])(f: A ~> B): F[B]
}
object FunctorK extends K11.Derivable[FunctorK] {

implicit def id[X]: FunctorK[K11.Id[X]] =
new FunctorK[K11.Id[X]] {
override def mapK[A[_], B[_]](a: K11.Id[X][A])(f: A ~> B): K11.Id[X][B] = f(a)
}

override inline implicit def genProduct[F[_[_]]](implicit m: K11.ProductGeneric[F]): Derived[FunctorK[F]] = {
val inst = K11.ProductInstances.of[F, FunctorK]

Derived {
new FunctorK[F] {
override def mapK[A[_], B[_]](a: F[A])(f: A ~> B): F[B] =
inst.withInstance(a).mapInstantiate { [t[_[_]]] => (i: FunctorK[t], t: t[A]) => i.mapK(t)(f) }
}
}
}

override inline implicit def genSum[F[_[_]]](implicit m: K11.SumGeneric[F]): Derived[FunctorK[F]] = {
val inst = K11.SumInstances.of[F, FunctorK]

Derived {
new FunctorK[F] {
override def mapK[A[_], B[_]](a: F[A])(f: A ~> B): F[B] =
inst.withInstance(a).inst.mapK(a)(f)
}
}
}

}

// =====| Instances |=====

implicit val productSimpleFunctor: FunctorK[ProductSimple] = FunctorK.derive
implicit val sumSimpleFunctor: FunctorK[SumSimple] = FunctorK.derive

// =====| Test |=====

// override def logLevel: Logger.LogLevel = Logger.LogLevel.Debug

override def spec: TestSpec =
suite("ExampleK11DerivationSpec")(
suite("ProductSimple")(
test("works-1") {
val input = ProductSimple[K11.Identity](
string = "string",
int = 1,
boolean = true,
optString = "string2".some,
optInt = 2.some,
optBoolean = false.some,
)
val mapped = productSimpleFunctor.mapK[K11.Identity, Option](input) { [t] => (a: K11.Identity[t]) => a.some }
val exp = ProductSimple[Option](
string = "string".some,
int = 1.some,
boolean = true.some,
optString = "string2".some.some,
optInt = 2.some.some,
optBoolean = false.some.some,
)

assertTrue(mapped == exp)
},
),
)

}
Loading

0 comments on commit d7dba77

Please sign in to comment.