aboutsummaryrefslogtreecommitdiff
path: root/tests/run
diff options
context:
space:
mode:
authorOlivier Blanvillain <olivier.blanvillain@gmail.com>2017-02-20 19:09:59 +0100
committerOlivier Blanvillain <olivier.blanvillain@gmail.com>2017-02-20 19:09:59 +0100
commit0e6fd1e078caffca39c5aa30753247d142affbd5 (patch)
treec5ba0a346bdb8b0e65de04cb30621544ac81b64b /tests/run
parentf467be62da8978e506f58b702b84e74ef7ce09de (diff)
downloaddotty-0e6fd1e078caffca39c5aa30753247d142affbd5.tar.gz
dotty-0e6fd1e078caffca39c5aa30753247d142affbd5.tar.bz2
dotty-0e6fd1e078caffca39c5aa30753247d142affbd5.zip
Add HMap test case
Diffstat (limited to 'tests/run')
-rw-r--r--tests/run/hmap.scala117
1 files changed, 117 insertions, 0 deletions
diff --git a/tests/run/hmap.scala b/tests/run/hmap.scala
new file mode 100644
index 000000000..b76668faa
--- /dev/null
+++ b/tests/run/hmap.scala
@@ -0,0 +1,117 @@
+trait Tuple
+trait ::[H, T <: Tuple] extends Tuple
+case class TupleCons[H, T <: Tuple](h: H, t: T) extends ::[H, T]
+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 :: T, Zero, H] =
+ new At[H :: T, Zero, H] {
+ def apply(l: H :: T): H = {
+ val TupleCons(h, _) = l
+ h
+ }
+ }
+
+ implicit def caseN[H, T <: Tuple, N <: Nat, O]
+ (implicit a: At[T, N, O]): At[H :: T, Succ[N], O] =
+ new At[H :: T, Succ[N], O] {
+ def apply(l: H :: T): O = {
+ val TupleCons(_, 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] :: T, Zero] = null
+
+ implicit def getTail[K, H, T <: Tuple, I <: Nat]
+ (implicit t: PhantomGet[K, T, I])
+ : PhantomGet[K, H :: 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
+ }
+
+ implicit class EntryAssoc[K](k: K) {
+ def -- [V](value: V): HEntry[K, V] = HEntry(value)
+ }
+
+ type --[A, B] = HEntry[A, B]
+ }
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ type MapType1 =
+ HEntry["name", String] ::
+ HEntry["genre", Boolean] ::
+ HEntry["moneyz", Int] ::
+ HEntry["cat", String] ::
+ TNil.type
+
+ // Since
+ val map1: MapType1 =
+ TupleCons(HEntry[K = "name"]("foo"),
+ TupleCons(HEntry[K = "genre"](true),
+ TupleCons(HEntry[K = "moneyz"](123),
+ TupleCons(HEntry[K = "cat"]("bar"),
+ TNil))))
+
+ import syntax.hmap._
+
+ assert(map1.get("name") == "foo")
+ assert(map1.get("genre") == true)
+ assert(map1.get("moneyz") == 123)
+ assert(map1.get("cat") == "bar")
+
+ type MapType2 =
+ "name" -- String ::
+ "genre" -- Boolean ::
+ "moneyz" -- Int ::
+ "cat" -- String ::
+ TNil.type
+
+ val map2: MapType2 =
+ TupleCons("name" -- "foo",
+ TupleCons("genre" -- true,
+ TupleCons("moneyz" -- 123,
+ TupleCons("cat" -- "bar",
+ TNil))))
+
+ assert(map2.get("name") == "foo")
+ assert(map2.get("genre") == true)
+ assert(map2.get("moneyz") == 123)
+ assert(map2.get("cat") == "bar")
+ }
+}