Возможно ли иметь HMap от типа A до нескольких других типов? - PullRequest
0 голосов
/ 27 февраля 2019

Shapeless имеет HMap s для обеспечения безопасности типов гетерогенных карт, но, похоже, он не позволяет отображать определенный тип на несколько типов.

Другими словами,это действительно:

class BiMapIS[K, V]
implicit val stringToInt = new BiMapIS[String, Int]
implicit val intToString = new BiMapIS[Int, String]

val hm = HMap[BiMapIS](23 -> "foo", "bar" -> 13)

Но это не так:

class BiMapIS[K, V]
implicit val stringToInt    = new BiMapIS[String, Int]
implicit val stringToString = new BiMapIS[String, String]

val hm = HMap[BiMapIS]("val1" -> 1, "val2" -> "two")

Мой вопрос: Есть ли способ разрешить безопасные сопоставления типов из одного типа (например, String) для нескольких типов (например, String и Int)?

Кроме того, я не женат на Shapeless для этого решения.

1 Ответ

0 голосов
/ 27 февраля 2019

Правильный способ сделать это требует некоторой сантехники: используйте Coproduct, чтобы перечислить все возможные значения:

class BiMapIS[K, V] { type Value = V }
implicit object strKey extends BiMapIS[String, Coproduct.`Int, String`.T]
implicit object intKey extends BiMapIS[Int,    Coproduct.`Boolean`.T]

val hm = HMap[BiMapIS](
  "val1" -> Coproduct[strKey.Value](1),
  "val2" -> Coproduct[strKey.Value]("two"),
  3      -> Coproduct[intKey.Value](true)
)

Затем вы можете сделать себя полезным классом ops для облегчения получения значений изHMap:

implicit class HMapOps[R[_,_]](hm: HMap[R]) {
  def atKey[K,C <: Coproduct](k: K)(implicit ev1: R[K, C]) = new {
    def withValueType[V](implicit ev2: shapeless.ops.coproduct.Selector[C, V]): Option[V] = 
      hm.get(k).flatMap(_.select[V])
  }
}

Собираем все вместе:

scala> hm.atKey("val1").withValueType[Int]
res1: Option[Int] = Some(1)

scala> hm.atKey("val1").withValueType[String]
res2: Option[String] = None

scala> hm.atKey("val1").withValueType[Boolean]   // String never maps to Boolean!!!
<console>: error: could not find implicit value for parameter ev2: shapeless.ops.coproduct.Selector[shapeless.:+:[Int,shapeless.:+:[String,shapeless.CNil]],Boolean]
       hm.atKey("val1").withValueType[Boolean]
...