Skip to content

Commit

Permalink
Added K1 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 92471ef commit 23b3bac
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,53 @@ import scala.reflect.ClassTag

abstract class K0T[UB] {

type Instances1[T <: Tuple, F[_ <: UB]] <: Tuple =
T match {
case EmptyTuple => EmptyTuple
case x *: xs => F[x] *: Instances1[xs, F]
}
type Instances2[T <: Tuple, F[_], G[_ <: UB]] <: Tuple =
type Kind[C, O <: UB] = C {
type MirroredType = O
type MirroredMonoType = O
type MirroredElemTypes <: 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 FieldInstances[T <: Tuple, W[_], F[_ <: UB]] <: Tuple =
T match {
case EmptyTuple => EmptyTuple
case x *: xs => F[G[x]] *: Instances2[xs, F, G]
case x *: xs => W[F[x]] *: FieldInstances[xs, W, F]
}

inline def summonInstances1[T <: Tuple, F[_]]: List[F[UB]] =
summonAll[Instances1[T, F]].toIArray.toList.asInstanceOf[List[F[UB]]]
inline def summonInstances2[T <: Tuple, F[_], G[_]]: List[F[G[UB]]] =
summonAll[Instances2[T, F, G]].toIArray.toList.asInstanceOf[List[F[G[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[A, F[_ <: UB]](
mirror: Mirror.ProductOf[A],
ev: A <:< Product,
rawInstances: List[LazyDerived[F[UB]]],
final case class ProductInstances[F <: UB, T[_ <: UB]](
m: Mirror.ProductOf[F],
ev: F <:< Product,
rawInstances: List[LazyDerived[T[UB]]],
) {

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

final case class withInstance(a: A) {
final case class withInstance(a: F) {

private def productElements: List[UB] = ev(a).productIterator.toList.asInstanceOf[List[UB]]

object map {

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

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

}

object foldLeft {

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

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

}
Expand All @@ -60,10 +63,10 @@ abstract class K0T[UB] {

object foldLeft {

def apply[R](z: R)(f: [t <: UB] => (R, F[t]) => R): R =
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[A], z: R)(f: [t <: UB] => (R, String, F[t]) => R): R =
def withLabels[R](labels: Labelling[F], 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) }

}
Expand All @@ -72,53 +75,53 @@ abstract class K0T[UB] {

}
object ProductInstances {
inline given of[A, F[_]](using m: Mirror.ProductOf[A]): ProductInstances[A, F] =
ProductInstances[A, F](
inline given of[F <: UB, T[_ <: UB]](using m: ProductGeneric[F]): ProductInstances[F, T] =
ProductInstances[F, T](
m,
summonInline[A <:< Product],
summonAll[Instances2[m.MirroredElemTypes, LazyDerived, F]].toIArray.toList.asInstanceOf[List[LazyDerived[F[UB]]]],
summonInline[F <:< Product],
// summonAll[FieldInstances[m.MirroredElemTypes, LazyDerived, F]].toIArray.toList.asInstanceOf[List[LazyDerived[F[UB]]]],
summonFieldInstances[m.MirroredElemTypes, LazyDerived, T],
)
}

// TODO (KR) : does sum need to be lazy as well?
final case class SumInstances[A, F[_]](
mirror: Mirror.SumOf[A],
children: List[F[Any]],
final case class SumInstances[F <: UB, T[_ <: UB]](
m: Mirror.SumOf[F],
children: List[T[UB]],
) {

def narrow[G[B] <: F[B]](implicit fCt: ClassTag[F[Any]], gCt: ClassTag[G[Any]]): SumInstances[A, G] =
SumInstances[A, G](
mirror,
def narrow[G[B <: UB] <: T[B]](implicit fCt: ClassTag[T[UB]], gCt: ClassTag[G[UB]]): SumInstances[F, G] =
SumInstances[F, G](
m,
children.asInstanceOf[List[Matchable]].map {
case gCt(c) => c
case other => throw new RuntimeException(s"Unable to narrow ${fCt.runtimeClass.getName} to ${gCt.runtimeClass.getName} ($other)")
},
)

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

}
object SumInstances {
inline given of[A, F[_]](using m: Mirror.SumOf[A]): SumInstances[A, F] =
inline given of[A <: UB, F[_ <: UB]](using m: SumGeneric[A]): SumInstances[A, F] =
SumInstances[A, F](
m,
summonAll[Instances2[m.MirroredElemTypes, Derived, F]].toIArray.toList.asInstanceOf[List[Derived[F[Any]]]].map(_.derived),
summonFieldInstances[m.MirroredElemTypes, Derived, F].map(_.derived),
)
}

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

inline implicit def genProduct[A](implicit m: Mirror.ProductOf[A]): Derived[F[A]]
inline implicit def genProduct[F <: UB](implicit m: ProductGeneric[F]): Derived[T[F]]

inline implicit def genSum[A](implicit m: Mirror.SumOf[A]): Derived[F[A]]
inline implicit def genSum[F <: UB](implicit m: SumGeneric[F]): Derived[T[F]]

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

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package harness.deriving

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

abstract class K1T[UB] {

type Kind[C, O[_ <: UB]] = C {
type MirroredType[X <: UB] = O[X]
type MirroredMonoType = O[UB]
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[x <: UB] = x

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

type FieldInstances[T <: [_ <: UB] =>> Tuple, F[_], G[_[_ <: UB]]] <: Tuple =
T[UB] match {
case ? *: ? => F[G[[X] =>> Head[T, X]]] *: FieldInstances[[X] =>> 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]]]]

// 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]]],
) {

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

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] =
m.asInstanceOf[Mirror.ProductOf[F[B]]]
.fromTuple(Tuple.fromArray(map(f).toArray).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)

}

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[UB]) => 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] =
ProductInstances[F, T](
m,
summonInline[ProductGeneric[F]#MirroredMonoType <:< Product],
summonFieldInstances[m.MirroredElemTypes, LazyDerived, T],
)
}

final case class SumInstances[F[_ <: UB], T[_[_ <: UB]]](
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,
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] =
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 K1 extends K1T[Any]
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package harness.deriving

import harness.core.*
import harness.deriving.ExampleTypes.*
import harness.deriving.ExampleK0Types.*
import harness.zio.*
import harness.zio.test.*
import scala.deriving.Mirror.{ProductOf, SumOf}
import zio.test.*
import zio.test.Assertion.*

object ExampleDerivationSpec extends DefaultHarnessSpec {
object ExampleK0DerivationSpec extends DefaultHarnessSpec {

trait Show[A] {
def show(a: A): IndentedString
Expand Down Expand Up @@ -76,7 +76,7 @@ object ExampleDerivationSpec extends DefaultHarnessSpec {
// override def logLevel: Logger.LogLevel = Logger.LogLevel.Debug

override def spec: TestSpec =
suite("ExampleDerivationSpec")(
suite("ExampleK0DerivationSpec")(
makeSuite("ProductSimple")(productSimpleInstance, ProductSimple.instances),
makeSuite("SumSimple")(sumSimpleInstance, SumSimple.instances),
makeSuite("SelfRecursiveProduct")(selfRecursiveProductInstance, SelfRecursiveProduct.instances),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package harness.deriving

import cats.syntax.option.*

object ExampleTypes {
object ExampleK0Types {

/*
implicit val productSimpleInstance: MyTypeClass[ProductSimple] = MyTypeClass.derive
Expand Down

0 comments on commit 23b3bac

Please sign in to comment.