Skip to content

Instantly share code, notes, and snippets.

@Astrac
Created July 18, 2017 15:56
Show Gist options
  • Save Astrac/a040aeed6e9f54b59d887590ffc23501 to your computer and use it in GitHub Desktop.
Save Astrac/a040aeed6e9f54b59d887590ffc23501 to your computer and use it in GitHub Desktop.
import shapeless._
import shapeless.ops.coproduct.Inject
import shapeless.ops.record.Selector
trait Distinct[U <: Coproduct] extends DepFn1[U] {
type Out <: Coproduct
}
trait DistinctLowPriority {
implicit def take[U <: Coproduct, X](implicit rest: Distinct[U]): Distinct.Aux[X :+: U, X :+: rest.Out] =
new Distinct[X :+: U] {
type Out = X :+: rest.Out
def apply(xu: X :+: U): Out = xu.eliminate(x => Inl(x), u => Inr(rest(u)))
}
}
object Distinct extends DistinctLowPriority {
type Aux[U <: Coproduct, O <: Coproduct] = Distinct[U] { type Out = O }
def apply[U <: Coproduct](implicit distinct: Distinct[U]): Aux[U, distinct.Out] = distinct
implicit val end: Aux[CNil, CNil] = new Distinct[CNil] {
type Out = CNil
def apply(u: CNil): CNil = u.impossible
}
implicit def drop[U <: Coproduct, X](
implicit rest: Distinct[U],
member: Inject[U, X]
): Aux[X :+: U, rest.Out] =
new Distinct[X :+: U] {
type Out = rest.Out
def apply(xu: X :+: U): rest.Out = rest(xu.eliminate(x => member(x), u => u))
}
}
trait Property[T, K] {
type Out
def get(t: T): Out
}
trait LowPriorityProperty {
implicit def forCNil[K]: Property.Aux[CNil, K, CNil] = new Property[CNil, K] {
type Out = CNil
def get(t: CNil) = sys.error("Unexpected evaluation of Property[CNil, K]")
}
implicit def forCoproductWithMissingSomeTail[H, OH, C <: Coproduct, OC <: Coproduct, K, O <: Coproduct](
implicit hProperty: Property.Aux[H, K, OH],
cProperty: Property.Aux[C, K, Option[OC]],
distinct: Distinct.Aux[OH :+: OC, O]
): Property.Aux[H :+: C, K, Option[O]] =
new Property[H :+: C, K] {
type Out = Option[O]
def get(t: H :+: C) =
t.eliminate(
h => Some(Coproduct[OH :+: OC](hProperty.get(h))),
c => cProperty.get(c).map(_.extendLeft[OH])
)
.map(distinct(_))
}
implicit def forCoproductMissingHead[H, C <: Coproduct, OC <: Coproduct, K, O <: Coproduct](
implicit cProperty: Property.Aux[C, K, OC],
distinct: Distinct.Aux[OC, O]
): Property.Aux[H :+: C, K, Option[O]] =
new Property[H :+: C, K] {
type Out = Option[O]
def get(t: H :+: C) =
t.eliminate(h => None, c => Some(cProperty.get(c))).map(distinct(_))
}
}
object Property extends LowPriorityProperty {
type Aux[T, K, O] = Property[T, K] { type Out = O }
implicit def forCoproduct[H, OH, C <: Coproduct, OC <: Coproduct, K, O <: Coproduct](
implicit hProperty: Property.Aux[H, K, OH],
cProperty: Property.Aux[C, K, OC],
distinct: Distinct.Aux[OH :+: OC, O]
): Property.Aux[H :+: C, K, O] =
new Property[H :+: C, K] {
type Out = O
def get(t: H :+: C) = distinct(
t.eliminate(
h => Coproduct[OH :+: OC](hProperty.get(h)),
c => cProperty.get(c).extendLeft[OH]
)
)
}
implicit def forGenericCoproduct[T, TRepr <: Coproduct, K](
implicit gen: Generic.Aux[T, TRepr],
p: Property[TRepr, K]
): Property.Aux[T, K, p.Out] =
new Property[T, K] {
type Out = p.Out
def get(t: T) = p.get(gen.to(t))
}
implicit def forHList[T, TRepr <: HList, K](
implicit gen: LabelledGeneric.Aux[T, TRepr],
sel: Selector[TRepr, K]
): Property.Aux[T, K, sel.Out] =
new Property[T, K] {
type Out = sel.Out
def get(t: T) = sel(gen.to(t))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment