aboutsummaryrefslogtreecommitdiff
path: root/tests/run
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-03-02 10:16:32 +0100
committerMartin Odersky <odersky@gmail.com>2017-03-02 10:16:32 +0100
commit4c6a69ec075de6948f2c82778f6ccf7a30b6bc70 (patch)
treee7907465e8028e70b8c465785ef2060be2cd4bb4 /tests/run
parent0a839b80aaed89dfd0d84fa3308b72de590171cb (diff)
downloaddotty-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.
Diffstat (limited to 'tests/run')
-rw-r--r--tests/run/hmap-covariant.scala97
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")
+ }
+}