diff options
author | Martin Odersky <odersky@gmail.com> | 2017-03-02 10:16:32 +0100 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2017-03-02 10:16:32 +0100 |
commit | 4c6a69ec075de6948f2c82778f6ccf7a30b6bc70 (patch) | |
tree | e7907465e8028e70b8c465785ef2060be2cd4bb4 | |
parent | 0a839b80aaed89dfd0d84fa3308b72de590171cb (diff) | |
download | dotty-4c6a69ec075de6948f2c82778f6ccf7a30b6bc70.tar.gz dotty-4c6a69ec075de6948f2c82778f6ccf7a30b6bc70.tar.bz2 dotty-4c6a69ec075de6948f2c82778f6ccf7a30b6bc70.zip |
New test: covariant hmaps
Type inference tends to take quite different paths for non-variant
and variant data structures. Since, non-variant hmap has already exposed quite
a few problems, it's good to test it also in the covariant case.
-rw-r--r-- | tests/run/hmap-covariant.scala | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/tests/run/hmap-covariant.scala b/tests/run/hmap-covariant.scala new file mode 100644 index 000000000..475cc6ee6 --- /dev/null +++ b/tests/run/hmap-covariant.scala @@ -0,0 +1,97 @@ +trait Tuple +case class TCons[+H, +T <: Tuple](h: H, t: T) extends Tuple +final case object TNil extends Tuple + +// Type level natural numbers ------------------------------------------------- + +sealed trait Nat +sealed trait Succ[P <: Nat] extends Nat +sealed trait Zero extends Nat + +// Accessor type class to compute the N'th element of an Tuple L -------------- + +trait At[L <: Tuple, N <: Nat, Out] { + def apply(l: L): Out +} + +object At { + implicit def caseZero[H, T <: Tuple]: At[H TCons T, Zero, H] = + new At[H TCons T, Zero, H] { + def apply(l: H TCons T): H = { + val (h TCons _) = l + h + } + } + + implicit def caseN[H, T <: Tuple, N <: Nat, O] + (implicit a: At[T, N, O]): At[H TCons T, Succ[N], O] = + new At[H TCons T, Succ[N], O] { + def apply(l: H TCons T): O = { + val (_ TCons t) = l + a(t) + } + } +} + +// An HMap is an Tuple with HEntry elements. We are reusing Tuple for it's nice syntax + +final case class HEntry[K, V](value: V) + +// Accessor type class to compute the element of type K in a HMap L ----------- + +trait PhantomGet[K, M <: Tuple, I <: Nat] // extends PhantomAny + +object PhantomGet { + implicit def getHead[K, V, T <: Tuple] + : PhantomGet[K, HEntry[K, V] TCons T, Zero] = null + + implicit def getTail[K, H, T <: Tuple, I <: Nat] + (implicit t: PhantomGet[K, T, I]) + : PhantomGet[K, H TCons T, Succ[I]] = null +} + +// Syntax --------------------------------------------------------------------- + +object syntax { + object hmap { + implicit class HmapGet[M <: Tuple](m: M) { + def get[K, V, I <: Nat](k: K) + (implicit + g: PhantomGet[k.type, M, I], + a: At[M, I, HEntry[k.type, V]] + ): V = a(m).value + } + + def --[K, V](key: K, value: V) = HEntry[key.type, V](value) + } +} + +object Test { + def main(args: Array[String]): Unit = { + import syntax.hmap._ + + val map1 = + TCons(HEntry[K = "name"]("foo"), + TCons(HEntry[K = "genre"](true), + TCons(HEntry[K = "moneyz"](123), + TCons(HEntry[K = "cat"]("bar"), + (TNil: TNil.type))))) + + assert(map1.get("name") == "foo") + assert(map1.get("genre") == true) + assert(map1.get("moneyz") == 123) + assert(map1.get("cat") == "bar") + + val map2 = + TCons(--("name" , "foo"), + TCons(--("genre" , true), + TCons(--("moneyz", 123), + TCons(--("cat" , "bar"), + TNil)))) + + assert(map2.get("name") == "foo") + assert(map2.get("genre") == true) + assert(map2.get("moneyz") == 123) + assert(map2.get("cat") == "bar") + } +} |