From 0e6fd1e078caffca39c5aa30753247d142affbd5 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Mon, 20 Feb 2017 19:09:59 +0100 Subject: Add HMap test case --- tests/run/hmap.scala | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 tests/run/hmap.scala (limited to 'tests/run') 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") + } +} -- cgit v1.2.3 From bee890af9c1105f8cb3558715d80f6212f4639c0 Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Mon, 20 Feb 2017 19:51:12 +0100 Subject: Update HMap test --- tests/run/hmap.scala | 68 +++++++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 44 deletions(-) (limited to 'tests/run') diff --git a/tests/run/hmap.scala b/tests/run/hmap.scala index b76668faa..d84419ce1 100644 --- a/tests/run/hmap.scala +++ b/tests/run/hmap.scala @@ -1,6 +1,5 @@ trait Tuple -trait ::[H, T <: Tuple] extends Tuple -case class TupleCons[H, T <: Tuple](h: H, t: T) extends ::[H, T] +case class TCons[H, T <: Tuple](h: H, t: T) extends Tuple final case object TNil extends Tuple // Type level natural numbers ------------------------------------------------- @@ -16,19 +15,19 @@ trait At[L <: Tuple, N <: Nat, 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 + 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 :: T, Succ[N], O] = - new At[H :: T, Succ[N], O] { - def apply(l: H :: T): O = { - val TupleCons(_, t) = l + (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) } } @@ -44,11 +43,11 @@ 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 + : 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 :: T, Succ[I]] = null + : PhantomGet[K, H TCons T, Succ[I]] = null } // Syntax --------------------------------------------------------------------- @@ -63,50 +62,31 @@ object syntax { ): 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] + def --[K, V](key: K, value: V) = HEntry[key.type, V](value) } } 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._ + 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") - 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", + val map2 = + TCons(--("name" , "foo"), + TCons(--("genre" , true), + TCons(--("moneyz", 123), + TCons(--("cat" , "bar"), TNil)))) assert(map2.get("name") == "foo") -- cgit v1.2.3