在运行时恢复依赖类型
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)
}
pairs
类型是 Pair
的 Seq
(带有一些嵌入的未定义类型 key: Key[_]
)
key
类型为 Key[V]
,预期结果类型为 Option[V]
- 尝试 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 驱动的助手,如果你走那条路的话)
可能有一种巧妙的方式来表达您的要求而不会出现类型问题,但通常类型信息必须对调用者可见或在运行时检查。
我正试图在 运行 时恢复 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)
}
pairs
类型是Pair
的Seq
(带有一些嵌入的未定义类型key: Key[_]
)key
类型为Key[V]
,预期结果类型为Option[V]
- 尝试 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 驱动的助手,如果你走那条路的话)
可能有一种巧妙的方式来表达您的要求而不会出现类型问题,但通常类型信息必须对调用者可见或在运行时检查。