Created
July 18, 2017 15:56
-
-
Save Astrac/a040aeed6e9f54b59d887590ffc23501 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 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