io.hireproof.structure.Evidence.scala Maven / Gradle / Ivy
The newest version!
package io.hireproof.structure
import shapeless._
import shapeless.ops.coproduct.{Align, Reverse}
import shapeless.ops.hlist.{Init, Last, Prepend}
import scala.annotation.nowarn
object Evidence {
trait Product[A] {
type Out
def to(a: A): Out
def from(out: Out): A
}
object Product {
trait Merger[A] {
type Out
def to(a: A): Out
def from(out: Out): A
}
object Merger extends Merger1 {
type Aux[A, O] = Merger[A] { type Out = O }
implicit def discardLeft[A]: Merger.Aux[Unit |*| A, A] = new Merger[Unit |*| A] {
override type Out = A
override def to(a: (Unit, A)): A = a._2
override def from(out: A): (Unit, A) = ((), out)
}
implicit def discardRight[A]: Merger.Aux[A |*| Unit, A] = new Merger[A |*| Unit] {
override type Out = A
override def to(ab: A |*| Unit): A = ab._1
override def from(out: A): A |*| Unit = (out, ())
}
}
trait Merger1 {
implicit def fallback[A]: Merger.Aux[A, A] = new Merger[A] {
override type Out = A
override def to(a: A): A = a
override def from(out: A): A = out
}
}
def apply[A](implicit evidence: Evidence.Product[A]): evidence.type = evidence
type Aux[A, O] = Product[A] { type Out = O }
implicit def base[A]: Evidence.Product.Aux[A :: HNil, A] = new Product[A :: HNil] {
type Out = A
def to(a: A :: HNil): A = a.head
def from(out: Out): Out :: HNil = out :: HNil
}
implicit def inductive[A <: HList, B <: HList, C, D](implicit
init: Init.Aux[A, B],
last: Last.Aux[A, C],
evidence: Evidence.Product.Aux[B, D],
prepend: Prepend.Aux[B, C :: HNil, A]
): Evidence.Product.Aux[A, (D, C)] = new Product[A] {
type Out = (D, C)
def from(out: Out): A = evidence.from(out._1) :+ out._2
def to(a: A): Out = (evidence.to(init(a)), last(a))
}
implicit def generic[A, B <: HList, C](implicit
generic: Generic.Aux[A, B],
evidence: Evidence.Product.Aux[B, C]
): Evidence.Product.Aux[A, C] = new Product[A] {
type Out = C
def to(a: A): Out = evidence.to(generic.to(a))
def from(out: Out): A = generic.from(evidence.from(out))
}
}
trait Sum[A] {
type Out
def to(a: A): Out
def from(out: Out): A
}
object Sum extends Sum1 {
def apply[A](implicit evidence: Evidence.Sum[A]): evidence.type = evidence
type Aux[A, O] = Sum[A] { type Out = O }
implicit def base[A]: Evidence.Sum.Aux[A :+: CNil, A] = new Sum[A :+: CNil] {
override type Out = A
override def to(a: A :+: CNil): A = a.eliminate(identity, _.impossible)
override def from(a: A): A :+: CNil = Inl(a)
}
implicit def inductive[A, B <: Coproduct, C](implicit
evidence: Evidence.Sum.Aux[B, C]
): Evidence.Sum.Aux[A :+: B, Either[C, A]] = new Sum[A :+: B] {
override type Out = Either[C, A]
override def to(a: A :+: B): Either[C, A] = a match {
case Inl(a) => Right(a)
case Inr(b) => Left(evidence.to(b))
}
override def from(out: Either[C, A]): A :+: B = out match {
case Right(a) => Inl(a)
case Left(c) => Inr(evidence.from(c))
}
}
implicit def generic[A, B <: Coproduct, C <: Coproduct, D](implicit
generic: Generic.Aux[A, B],
reverseA: Reverse.Aux[B, C],
reverseB: Reverse.Aux[C, B],
evidence: Evidence.Sum.Aux[C, D]
): Evidence.Sum.Aux[A, D] = new Sum[A] {
override type Out = D
override def to(a: A): D = evidence.to(reverseA.apply(generic.to(a)))
override def from(d: D): A = generic.from(reverseB.apply(evidence.from(d)))
}
}
trait Sum1 {
@nowarn("msg=never used")
implicit def aligend[A, B <: Coproduct, C, D <: Coproduct, E](implicit
generic: Generic.Aux[A, B],
etc: EitherToCoproduct.Aux[C, D],
cte: CoproductToEither.Aux[D, C],
alignA: Align[B, D],
alignB: Align[D, B],
evidence: Evidence.Sum.Aux[D, C]
): Evidence.Sum.Aux[A, C] = new Sum[A] {
override type Out = C
override def to(a: A): C = evidence.to(alignA.apply(generic.to(a)))
override def from(out: C): A = generic.from(alignB.apply(evidence.from(out)))
}
}
trait EitherToCoproduct[A] {
type Out <: Coproduct
}
object EitherToCoproduct {
type Aux[A, O] = EitherToCoproduct[A] { type Out = O }
def apply[A](implicit evidence: EitherToCoproduct[A]): evidence.type = evidence
@nowarn("msg=never used")
implicit def base[A](implicit evidence: A <:!< Either[_, _]): Evidence.EitherToCoproduct.Aux[A, A :+: CNil] =
new EitherToCoproduct[A] {
override type Out = A :+: CNil
}
@nowarn("msg=never used")
implicit def inductive[A, B <: Coproduct, C](implicit
evidence: Evidence.EitherToCoproduct.Aux[A, B]
): Evidence.EitherToCoproduct.Aux[Either[A, C], C :+: B] = new EitherToCoproduct[Either[A, C]] {
override type Out = C :+: B
}
}
trait CoproductToEither[A] {
type Out
}
object CoproductToEither {
type Aux[A, O] = CoproductToEither[A] { type Out = O }
def apply[A](implicit evidence: CoproductToEither[A]): evidence.type = evidence
implicit def base[A]: Evidence.CoproductToEither.Aux[A :+: CNil, A] = new CoproductToEither[A :+: CNil] {
override type Out = A
}
@nowarn("msg=never used")
implicit def inductive[A, B <: Coproduct, C](implicit
evidence: Evidence.CoproductToEither.Aux[B, C]
): Evidence.CoproductToEither.Aux[A :+: B, Either[C, A]] = new CoproductToEither[A :+: B] {
override type Out = Either[C, A]
}
}
trait Enumeration[A] {
def values: Set[A]
}
object Enumeration {
def apply[A](implicit evidence: Evidence.Enumeration[A]): evidence.type = evidence
trait Aux[A, In] {
def values: List[A]
}
object Aux {
implicit def base[A]: Aux[A, CNil] = new Aux[A, CNil] {
override def values: List[A] = Nil
}
implicit def inductive[A, B <: A, C <: Coproduct](implicit
witness: Witness.Aux[B],
evidence: Aux[A, C]
): Aux[A, B :+: C] =
new Aux[A, B :+: C] {
override def values: List[A] = witness.value :: evidence.values
}
}
@nowarn("msg=never used")
implicit def generic[A, B <: Coproduct](implicit generic: Generic.Aux[A, B], aux: Aux[A, B]): Enumeration[A] =
new Enumeration[A] { override def values: Set[A] = aux.values.toSet }
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy