Skip to content

Commit 4c6a69e

Browse files
committed
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.
1 parent 0a839b8 commit 4c6a69e

File tree

1 file changed

+97
-0
lines changed

1 file changed

+97
-0
lines changed

tests/run/hmap-covariant.scala

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
trait Tuple
2+
case class TCons[+H, +T <: Tuple](h: H, t: T) extends Tuple
3+
final case object TNil extends Tuple
4+
5+
// Type level natural numbers -------------------------------------------------
6+
7+
sealed trait Nat
8+
sealed trait Succ[P <: Nat] extends Nat
9+
sealed trait Zero extends Nat
10+
11+
// Accessor type class to compute the N'th element of an Tuple L --------------
12+
13+
trait At[L <: Tuple, N <: Nat, Out] {
14+
def apply(l: L): Out
15+
}
16+
17+
object At {
18+
implicit def caseZero[H, T <: Tuple]: At[H TCons T, Zero, H] =
19+
new At[H TCons T, Zero, H] {
20+
def apply(l: H TCons T): H = {
21+
val (h TCons _) = l
22+
h
23+
}
24+
}
25+
26+
implicit def caseN[H, T <: Tuple, N <: Nat, O]
27+
(implicit a: At[T, N, O]): At[H TCons T, Succ[N], O] =
28+
new At[H TCons T, Succ[N], O] {
29+
def apply(l: H TCons T): O = {
30+
val (_ TCons t) = l
31+
a(t)
32+
}
33+
}
34+
}
35+
36+
// An HMap is an Tuple with HEntry elements. We are reusing Tuple for it's nice syntax
37+
38+
final case class HEntry[K, V](value: V)
39+
40+
// Accessor type class to compute the element of type K in a HMap L -----------
41+
42+
trait PhantomGet[K, M <: Tuple, I <: Nat] // extends PhantomAny
43+
44+
object PhantomGet {
45+
implicit def getHead[K, V, T <: Tuple]
46+
: PhantomGet[K, HEntry[K, V] TCons T, Zero] = null
47+
48+
implicit def getTail[K, H, T <: Tuple, I <: Nat]
49+
(implicit t: PhantomGet[K, T, I])
50+
: PhantomGet[K, H TCons T, Succ[I]] = null
51+
}
52+
53+
// Syntax ---------------------------------------------------------------------
54+
55+
object syntax {
56+
object hmap {
57+
implicit class HmapGet[M <: Tuple](m: M) {
58+
def get[K, V, I <: Nat](k: K)
59+
(implicit
60+
g: PhantomGet[k.type, M, I],
61+
a: At[M, I, HEntry[k.type, V]]
62+
): V = a(m).value
63+
}
64+
65+
def --[K, V](key: K, value: V) = HEntry[key.type, V](value)
66+
}
67+
}
68+
69+
object Test {
70+
def main(args: Array[String]): Unit = {
71+
import syntax.hmap._
72+
73+
val map1 =
74+
TCons(HEntry[K = "name"]("foo"),
75+
TCons(HEntry[K = "genre"](true),
76+
TCons(HEntry[K = "moneyz"](123),
77+
TCons(HEntry[K = "cat"]("bar"),
78+
(TNil: TNil.type)))))
79+
80+
assert(map1.get("name") == "foo")
81+
assert(map1.get("genre") == true)
82+
assert(map1.get("moneyz") == 123)
83+
assert(map1.get("cat") == "bar")
84+
85+
val map2 =
86+
TCons(--("name" , "foo"),
87+
TCons(--("genre" , true),
88+
TCons(--("moneyz", 123),
89+
TCons(--("cat" , "bar"),
90+
TNil))))
91+
92+
assert(map2.get("name") == "foo")
93+
assert(map2.get("genre") == true)
94+
assert(map2.get("moneyz") == 123)
95+
assert(map2.get("cat") == "bar")
96+
}
97+
}

0 commit comments

Comments
 (0)