在运行时恢复依赖类型

Restore a dependent type at runtime

我正试图在 运行 时恢复 Scala 中的依赖类型。我基本上想存档一个类型保存映射,其中每个键都有一个关联的类型,但是存储的键值对的所有类型信息对 Map 的用户都是不可见的(与令人敬畏的 Shapeless Map 不同)。

class Key[V] {
  type Value = V
  def ->(value: V) = Pair(this, value)
}
trait Pair {
  val key: Key[_]
  val value: key.Value
}
trait Map {
  val pairs: Seq[Pair]
  def get[V](key: Key[V]): Option[V] =
    pairs.find(pair => pair.key eq key).map(_.value).asInstanceOf[Option[V]]
    //                          ^                              ^
    //   the runtime prove that pair.key.Value equals V        |
    //                                                         |
    //                        'Convince' the compile that I know what I do
}

用法:

val text = new Key[String]
val count = new Key[Int]
val map: Map = new Map { val pairs = text -> "Hello World!" :: Nil }

map.get(text)  // Some(Hello World!), Type: Option[String]
map.get(count) // None, Type: Option[Int]

是否可以编写 get 方法而不使用显式转换 asInstanceOf 或隐式转换与未检查分支的匹配?

我试图为对写 unapply,但 运行 遇到了同样的问题。

请注意,我省略了 Pair-companion 对象的定义。这里有一个 运行ning 示例 Gist.

有很多方法可以解决您的类型问题。首先定义问题来源:

trait Map {
  val pairs: Seq[Pair]                     // (1)
  def get[V](key: Key[V]): Option[V] =     // (2)
    pairs.find(_.key eq key).map{_.value } // (3)
}
  1. pairs 类型是 PairSeq(带有一些嵌入的未定义类型 key: Key[_]
  2. key 类型为 Key[V],预期结果类型为 Option[V]
  3. 尝试 return 从 (1) Key[_] 中键入而不是预期的 Key[V] 并提取 V

解决方法:你应该保证 pairs 嵌入类型的 key 与你的 return

相同

可能的解决方案之一:

trait Key[V] {
  def ->(value: V) = Pair(this, value)
}

trait Pair {
  type Value
  val key: Key[Value]
  val value: Value
}

trait Map1[V] {
  val pairs: Seq[Pair {type Value = V } ]

  def get(key: Key[V]): Option[V] =
    pairs.find(_.key eq key).map{ _.value }
}

trait Map2 {
  type Value

  val pairs: Seq[Pair {type Value = Map2.this.Value} ]

  def get[V >: Map2.this.Value](key: Key[V]): Option[V] =
    pairs.find(_.key eq key).map{ _.value }
}

记住 JVM 在运行时擦除泛型。因此,任何依赖于泛型的事情,包括依赖类型,都只能发生在编译时——即在调用者中,因为任何给定的方法都只会编译到一个运行时代码路径。正如您所说,唯一的选择是检查运行时 class(直接或通过模式匹配)。 (Shapeless 有一个类型安全的、类型class 驱动的助手,如果你走那条路的话)

可能有一种巧妙的方式来表达您的要求而不会出现类型问题,但通常类型信息必须对调用者可见或在运行时检查。