Skip to content

Instantly share code, notes, and snippets.

@Astrac
Created September 24, 2015 09:42
Show Gist options
  • Save Astrac/82370b0794c98eeb99f0 to your computer and use it in GitHub Desktop.
Save Astrac/82370b0794c98eeb99f0 to your computer and use it in GitHub Desktop.
A map from types to values of other types
import shapeless._
import shapeless.ops.hlist._
sealed trait SameSize[L1 <: HList, L2 <: HList]
object SameSize {
def apply[L1 <: HList, L2 <: HList] = new SameSize[L1, L2] {}
implicit def hnilHasSameLength = SameSize[HNil, HNil]
implicit def hlistHasSameLength[L1 <: HList, L2 <: HList, L1T <: HList, L2T <: HList](implicit
l1IsHCons: IsHCons.Aux[L1, _, L1T],
l2IsHCons: IsHCons.Aux[L2, _, L2T],
recurse: SameSize[L1T, L2T]) = SameSize[L1, L2]
}
sealed trait IndexOfType[L <: HList, F] {
type Out <: Nat
}
object IndexOfType {
type Aux[L <: HList, T, O <: Nat] = IndexOfType[L, T] { type Out = O }
def apply[L <: HList, F, O <: Nat] = new IndexOfType[L, F] { type Out = O }
implicit def typeIsInTail[L <: HList, T <: HList, F, O <: Nat](implicit
isHCons: IsHCons.Aux[L, _, T],
findInTail: IndexOfType.Aux[T, F, O]): IndexOfType.Aux[L, F, Succ[O]] = IndexOfType[L, F, Succ[O]]
implicit def typeIsHead[L <: HList, H, F](implicit
isHCons: IsHCons.Aux[L, H, _],
ev: H =:= F): IndexOfType.Aux[L, F, _0] = IndexOfType[L, F, _0]
}
trait NotContains[L <: HList, F]
object NotContains {
def apply[L <: HList, F] = new NotContains[L, F] {}
implicit def hNilNotContains[F] = NotContains[HNil, F]
implicit def hTailNotContains[L <: HList, H, T <: HList, F](implicit
isHCons: IsHCons.Aux[L, H, T],
isNotH: H =:!= F,
isNotInHTail: NotContains[T, F]) = NotContains[L, F]
}
class TypeMap[KS <: HList, VS <: HList](val values: VS)(implicit sizeEq: SameSize[KS, VS]) {
sealed trait Getter[K] {
type Out
def value: Out
}
object Getter {
type Aux[K, O] = Getter[K] { type Out = O }
implicit def instance[K, KI <: Nat, O](implicit
kidx: IndexOfType.Aux[KS, K, KI],
res: At.Aux[VS, KI, O]): Getter.Aux[K, O] = new Getter[K] {
type Out = O
val value = res(values)
}
}
final class Setter[K] {
def apply[V](value: V)(implicit nc: NotContains[KS, K]) =
new TypeMap[K :: KS, V :: VS](value :: values)
}
final class Replacer[K] {
def apply[V, KI <: Nat, O <: HList, OV](v: V)(implicit
idx: IndexOfType.Aux[KS, K, KI],
old: At.Aux[VS, KI, OV],
rep: ReplaceAt.Aux[VS, KI, V, (OV, O)],
ss: SameSize[KS, O]) = new TypeMap[KS, O](rep(values, v)._2)
}
def get[K](implicit g: Getter[K]): g.Out = g.value
def set[K] = new Setter[K]
def replace[K] = new Replacer[K]
}
object TypeMap {
def empty = new TypeMap[HNil, HNil](HNil)
}
object Test {
val x = TypeMap.empty
.set[String](1)
.set[Boolean]("Foo")
.set[Double](true)
.replace[String](1.2)
val a: Double = x.get[String]
val b: String = x.get[Boolean]
val c: Boolean = x.get[Double]
// Does not compile
// x.get[Int] - No such a key
// x.replace[Int](2) - No such a key
// x.set[String]('c') - The key is already set
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment