Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Linear type class
package me.limansky
import shapeless.ops.hlist.Prepend
import shapeless.{::, <:!<, Generic, HList, HNil, LabelledGeneric, Lazy}
trait Linear[L] {
type Repr <: HList
def linear(t: L): Repr
}
trait LowPriorityLinear {
type Aux[T, R] = Linear[T] { type Repr = R }
implicit def notListLinear[T](implicit ev: T <:!< HList): Linear.Aux[T, T :: HNil] = new Linear[T] {
override type Repr = T :: HNil
override def linear(t: T): Repr = t :: HNil
}
}
object Linear extends LowPriorityLinear {
def apply[T](implicit ev: Linear[T]): Aux[T, ev.Repr] = ev
implicit val hnilLinear: Linear.Aux[HNil, HNil] = createLinear(_ => HNil)
implicit def hconsLinear[H, T <: HList, HR <: HList, TR <: HList](implicit
hLinear: Lazy[Linear.Aux[H, HR]],
tLinear: Lazy[Linear.Aux[T, TR]],
prepend: Prepend[HR, TR]
): Linear.Aux[H :: T, prepend.Out] = {
createLinear { case h :: t =>
prepend(hLinear.value.linear(h), tLinear.value.linear(t))
}
}
implicit def genericLinear[T, TR <: HList, R <: HList](implicit
gen: LabelledGeneric.Aux[T, TR],
lin: Lazy[Linear.Aux[TR, R]]
): Linear.Aux[T, R] = {
createLinear(t => lin.value.linear(gen.to(t)))
}
private def createLinear[T, R <: HList](f: T => R): Linear.Aux[T, R] = new Linear[T] {
override type Repr = R
override def linear(t: T): R = f(t)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment