Skip to content

Instantly share code, notes, and snippets.

@GrafBlutwurst
Created March 7, 2024 09:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GrafBlutwurst/3222b0c5b9b9e24304e5e809be627157 to your computer and use it in GitHub Desktop.
Save GrafBlutwurst/3222b0c5b9b9e24304e5e809be627157 to your computer and use it in GitHub Desktop.
import cats._
import cats.data.Const
import cats.tagless._
import cats.tagless.implicits._
trait GadtEncoded[Trait[_[_]], GADTRepresentation[_]] {
def gadtInstance: Trait[GADTRepresentation]
def gadtEval[F[_]](against: Trait[F]): GADTRepresentation ~> F
final def partialOverride[F[_]](base: Trait[F])(
partialOverride: GadtEncoded.PartialOverride[GADTRepresentation, F]
)(implicit functorK: FunctorK[Trait]): Trait[F] =
GadtEncoded.PartialOverride.genericApplyOverride(base)(partialOverride)(this, functorK)
final def partialOverrideWithSuper[F[_]](base: Trait[F])(
partialOverride: GadtEncoded.PartialOverrideWithSuper[GADTRepresentation, F]
)(implicit functorK: FunctorK[Trait]): Trait[F] =
GadtEncoded.PartialOverrideWithSuper.genericApplyOverride(base)(partialOverride)(this, functorK)
final def byNaturalTransformation[F[_]](nt: GADTRepresentation ~> F)(implicit
functorK: FunctorK[Trait]
): Trait[F] = functorK.mapK(gadtInstance)(nt)
final def const[C](value: C)(implicit functorK: FunctorK[Trait]): Trait[Const[C, *]] = {
val nt: GADTRepresentation ~> Const[C, *] = new (GADTRepresentation ~> Const[C, *]) {
override def apply[A](fa: GADTRepresentation[A]): Const[C, A] = Const[C, A](value)
}
byNaturalTransformation(nt)
}
final def mkConstError[M[_]](
msg: GADTRepresentation[_] => String
)(implicit M: MonadThrow[M], functorK: FunctorK[Trait]): Trait[M] =
byNaturalTransformation {
new (GADTRepresentation ~> M) {
override def apply[A](fa: GADTRepresentation[A]): M[A] =
MonadThrow[M].raiseError(new NotImplementedError(msg(fa)))
}
}
}
object GadtEncoded {
trait PartialOverride[G[_], H[_]] {
def apply[A](ga: G[A]): Option[H[A]]
}
object PartialOverride {
implicit def functorK[G[_]]: FunctorK[PartialOverride[G, *[_]]] =
new FunctorK[PartialOverride[G, *[_]]] {
def mapK[F[_], H[_]](af: PartialOverride[G, F])(fk: F ~> H): PartialOverride[G, H] =
new PartialOverride[G, H] {
override def apply[A](ga: G[A]): Option[H[A]] =
af(ga) match {
case None => None
case Some(value) => Some(fk(value))
}
}
}
def genericApplyOverride[Trait[_[_]], GADTRepresentation[_], F[_]](
base: Trait[F]
)(
partialOverride: PartialOverride[GADTRepresentation, F]
)(implicit
gadtEncode: GadtEncoded[Trait, GADTRepresentation],
functorK: FunctorK[Trait],
): Trait[F] = {
val chooseNt: GADTRepresentation ~> F = new (GADTRepresentation ~> F) {
private lazy val baseCalls = gadtEncode.gadtEval(base)
def apply[A](gadt: GADTRepresentation[A]): F[A] =
partialOverride(gadt).getOrElse(baseCalls(gadt))
}
gadtEncode.gadtInstance.mapK(chooseNt)
}
def emptyOverride[Trait[_[_]], GADTRepresentation[_], F[_]]
: PartialOverride[GADTRepresentation, F] =
new PartialOverride[GADTRepresentation, F] {
override def apply[A](ga: GADTRepresentation[A]): Option[F[A]] = None
}
}
trait PartialOverrideWithSuper[G[_], H[_]] {
def apply[A](ga: G[A], superCall: => H[A]): Option[H[A]]
}
object PartialOverrideWithSuper {
def genericApplyOverride[Trait[_[_]], GADTRepresentation[_], F[_]](
base: Trait[F]
)(
partialOverride: PartialOverrideWithSuper[GADTRepresentation, F]
)(implicit
gadtEncode: GadtEncoded[Trait, GADTRepresentation],
functorK: FunctorK[Trait],
): Trait[F] = {
val chooseNt: GADTRepresentation ~> F = new (GADTRepresentation ~> F) {
private lazy val baseCalls = gadtEncode.gadtEval(base)
def apply[A](gadt: GADTRepresentation[A]): F[A] =
partialOverride(gadt, baseCalls(gadt)).getOrElse(baseCalls(gadt))
}
gadtEncode.gadtInstance.mapK(chooseNt)
}
def emptyOverride[Trait[_[_]], GADTRepresentation[_], F[_]]
: PartialOverrideWithSuper[GADTRepresentation, F] =
new PartialOverrideWithSuper[GADTRepresentation, F] {
override def apply[A](ga: GADTRepresentation[A], superCall: => F[A]): Option[F[A]] = None
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment