Created
March 7, 2024 09:13
-
-
Save GrafBlutwurst/3222b0c5b9b9e24304e5e809be627157 to your computer and use it in GitHub Desktop.
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
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