-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
23b3bac
commit d7dba77
Showing
6 changed files
with
304 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
153 changes: 153 additions & 0 deletions
153
modules/harness-deriving/shared/src/main/scala/harness/deriving/K11.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] |
83 changes: 83 additions & 0 deletions
83
...es/harness-deriving/shared/src/test/scala/harness/deriving/ExampleK11DerivationSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}, | ||
), | ||
) | ||
|
||
} |
Oops, something went wrong.