Scala:映射的存在类型
我想在未知地图上使用不同类型的地图:Scala:映射的存在类型,scala,existential-type,Scala,Existential Type,我想在未知地图上使用不同类型的地图: val map: Map[Foo[A], Bar[A]] = ... ... val foo = new Foo[Qux] val bar: Bar[Qux] = map(foo) 这不起作用,因为A是未知的。我必须将其定义为: val map: Map[Foo[_], Bar[_]] = ... ... val foo = new Foo[Qux] val bar: Bar[Qux] = map(foo).asInstanceOf[Bar[Qux]]
val map: Map[Foo[A], Bar[A]] = ...
...
val foo = new Foo[Qux]
val bar: Bar[Qux] = map(foo)
这不起作用,因为A是未知的。我必须将其定义为:
val map: Map[Foo[_], Bar[_]] = ...
...
val foo = new Foo[Qux]
val bar: Bar[Qux] = map(foo).asInstanceOf[Bar[Qux]]
这很管用,但演员阵容很难看。我宁愿找个更好的办法。我想答案是对一些关键字使用存在类型,但我不清楚它是如何工作的。如果是:
Map[Foo[A], Bar[A]] forSome { type A }
或:
或:
事实上,这些都不管用
Map[Foo[A], Bar[A]] forSome { type A }
是一个映射
,其中所有键的类型都相同Foo[a]
,值的类型为Bar[a]
(但是对于这种类型的不同映射,类型可能不同);在第二和第三个示例中,Bar[A]
中的A
与某些下的A
完全不同
这种难看的解决方法应该有效:
// need type members, so can't use tuples
case class Pair[A, B](a: A, b: B) {
type T1 = A
type T2 = B
}
type PairedMap[P <: Pair[_, _]] = Map[P#T1, P#T2]
type FooBarPair[A] = Pair[Foo[A], Bar[A]]
val map: PairedMap[FooBarPair[_]] = ...
//需要类型成员,所以不能使用元组
案例类别对[A,B](A:A,B:B){
T1型=A型
T2型=B型
}
输入PairedMap[P类似的东西怎么样
def map[A]: Map[Foo[A], Bar[A]] = ...
val myMap = map[Qux]
...
val foo = new Foo[Qux]
val bar: Bar[Qux] = myMap(foo)
或者(受阿列克谢·罗曼诺夫回答的启发)
我想在未知的地图上使用不同类型的地图
因此,您需要一个带有以下界面的Map[K,V]
变体,对吗
trait DependentMap[K[_],V[_]] {
def add[A](key: K[A], value: V[A]): DependentMap[K,V]
def get[A](key: K[A]): Option[V[A]]
}
从类型签名可能有点难以区分,因此让我们创建几个虚拟值,看看类型检查器是否接受我们希望它接受的内容,并拒绝我们希望它拒绝的内容
// dummy definitions just to check that the types are correct
case class Foo[+A](a: A)
case class Bar[+A](a: A)
val myMap: DependentMap[Foo,Bar] = null
myMap.add(Foo( 42), Bar( 43)) // typechecks
myMap.add(Foo("foo"), Bar("bar")) // typechecks
myMap.add(Foo( 42), Bar("bar")) // type mismatch
val r1: Option[Bar[ Int]] = myMap.get(Foo( 42)) // typechecks
val r2: Option[Bar[String]] = myMap.get(Foo("foo")) // typechecks
val r3: Option[Bar[String]] = myMap.get(Foo( 42)) // type mismatch
到目前为止还不错。但看看一旦我们开始玩继承会发生什么:
val fooInt: Foo[Int] = Foo(42)
val fooAny: Foo[Any] = fooInt
val barStr: Bar[String] = Bar("bar")
val barAny: Bar[Any] = barStr
println(fooInt == fooAny) // true
myMap.add(fooAny, barAny).get(fooInt) // Bar("bar")?
由于fooInt
和fooAny
是相同的值,我们天真地希望这个get
成功。根据get
的类型签名,它必须使用类型为Bar[Int]
的值成功,但是这个值从何而来?我们输入的值具有类型为Bar[Any]
以及类型Bar[String]
,但肯定不是类型Bar[Int]
这是另一个令人惊讶的例子
val fooListInt: Foo[List[Int]] = Foo(List[Int]())
val fooListStr: Foo[List[String]] = Foo(List[String]())
println(fooListInt == fooListStr) // true!
myMap.add(fooListInt, Bar(List(42))).get(fooListStr) // Bar(List(42))?
这一次,doulistint
和douliststr
看起来应该是不同的,但实际上它们都有Nil
,类型为List[Nothing]
,它是List[Int]
和List[String]
的子类型。所以如果我们想模仿Map[K,V]的行为
在这样一个键上,get
必须再次成功,但它不能成功,因为我们给了它一个Bar[List[Int]
,它需要生成一个Bar[List[String]
这一切都说,我们的代码>依赖图不应该考虑键是相等的,除非类型<代码> A/<代码> < <代码> Addio>代码>也等于类型<代码> A<代码> < <代码>获取< /代码>。这里有一个实现来确保是这样的。
import scala.reflect.runtime.universe._
class DependentMap[K[_],V[_]](
inner: Map[
(TypeTag[_], Any),
Any
] = Map()
) {
def add[A](
key: K[A], value: V[A]
)(
implicit tt: TypeTag[A]
): DependentMap[K,V] = {
val realKey: (TypeTag[_], Any) = (tt, key)
new DependentMap(inner + ((realKey, value)))
}
def get[A](key: K[A])(implicit tt: TypeTag[A]): Option[V[A]] = {
val realKey: (TypeTag[_], Any) = (tt, key)
inner.get(realKey).map(_.asInstanceOf[V[A]])
}
}
这里有几个例子证明了它的预期效果
scala> val myMap: DependentMap[Foo,Bar] = new DependentMap
scala> myMap.add(Foo(42), Bar(43)).get(Foo(42))
res0: Option[Bar[Int]] = Some(Bar(43))
scala> myMap.add(Foo("foo"), Bar("bar")).get(Foo("foo"))
res1: Option[Bar[String]] = Some(Bar(bar))
scala> myMap.add(Foo(42), Bar("bar")).get(Foo(42))
error: type mismatch;
scala> myMap.add[Any](Foo(42), Bar("bar")).get(Foo(42))
res2: Option[Bar[Int]] = None
scala> myMap.add[Any](Foo(42), Bar("bar")).get[Any](Foo(42))
res3: Option[Bar[Any]] = Some(Bar(bar))
scala> myMap.add(Foo(List[Int]()), Bar(List(43))).get(Foo(List[Int]()))
res4: Option[Bar[List[Int]]] = Some(Bar(List(43)))
scala> myMap.add(Foo(List[Int]()), Bar(List(43))).get(Foo(List[String]()))
res5: Option[Bar[List[String]]] = None
这很管用,但演员阵容很难看。我宁愿找个更好的办法
那么您可能会对我的get
是使用cast实现的感到失望。让我试着说服您,没有其他方法
而不是一个可能包含或不包含密钥的映射,让我们考虑一个简单得多的例子。
def safeCast[A,B](
value: A
)(
implicit tt1: TypeTag[A], tt2: TypeTag[B]
): Option[B] = {
if (tt1 == tt2)
Some(value.asInstanceOf[B])
else
None
}
给定a的一个类型标记和B的一个类型标记,如果两个类型标记相等,那么我们知道a和B是相同的类型,因此类型转换是安全的。但是,如果没有类型转换,我们怎么可能实现这一点呢?相等检查返回的仅仅是布尔值,而不像中那样是相等的见证。因此,没有任何东西可以静态地区分如果
,编译器不可能知道无强制转换在“true”分支中是合法的,但在另一个分支中是非法的
在我们的DependentMap
更复杂的情况下,我们还必须将我们的类型标记与执行add
时存储的类型标记进行比较,因此我们还必须使用强制转换
我想答案是将存在类型与forSome
关键字一起使用
我明白你为什么会这样想。你需要一系列从键到值的关联,其中每个(键,值)对都有一些{type a}的类型(Foo[a],Bar[a])。事实上,如果你不关心性能,你可以将这些关联存储在一个列表中:
val myList: List[(Foo[A], Bar[A]) forSome {type A}]
由于某些
的位于括号内,因此允许列表中的每个条目使用不同的a。并且由于某些
的Foo[a]
和Bar[a]
都位于的左侧,因此在每个条目中a
必须匹配
但是,在Map的情况下,没有地方放置for some
来获得您想要的结果。Map的问题是它有两个类型参数,这使得我们无法将它们都放在for some
的左侧,而不将for some
放在括号外。这对d没有意义O:因为这两种类型的参数是独立的,没有任何东西将左类型参数的一个事件链接到相应的右类型参数的出现,因此没有办法知道哪个<代码> < < /代码> s应该匹配。请考虑下面的反例:
case class NotMap[K,V](k1: K, k2: K, v1: V, v2: V, v3: V)
K值的数量与V值的数量不同,因此特别是K值和V值之间没有对应关系
这将允许我们指定映射中的每个条目的A
s应该匹配,然后还可以使用该语法指定无意义类型NotMap[{Foo[A],Bar[A]}对于某些{type A}]
。因此没有这样的语法,我们需要使用除Map
之外的类型,例如DependentMap
或。谢谢……你知道如何实现我想要的吗?这很难看。它实际上从来没有生成FooBarPair,只是使用从中派生的类型?是的,它只是为该类型定义的。没有动作
def safeCast[A,B](
value: A
)(
implicit tt1: TypeTag[A], tt2: TypeTag[B]
): Option[B] = {
if (tt1 == tt2)
Some(value.asInstanceOf[B])
else
None
}
val myList: List[(Foo[A], Bar[A]) forSome {type A}]
case class NotMap[K,V](k1: K, k2: K, v1: V, v2: V, v3: V)