我有一张地图,其中键和值都是泛型类型。像这样:
Map[Foo[A], Bar[A]]
我想表达的是,A
类型对于地图中的每个键值对可能不同,但每个键的参数化始终与它映射到的值的类型相同。因此,Foo[Int]
始终映射到Bar[Int]
,Foo[String]
始终映射到Bar[String]
,依此类推。
有没有人知道表达这种方式的方法?
修改
以下是我正在尝试做的事情的一个例子:
trait Parameter // not important what it actually does
class Example {
val handlers: Map[_ <: Parameter, (_ <: Parameter) => _] = Map()
def doSomething() {
for ((value, handler) <- handlers) {
handler(value)
}
}
}
这个想法是一个值总是映射到一个可以接受它作为参数的函数,但是现在编写代码时,编译器无法知道这一点。
答案 0 :(得分:2)
事实证明,可以在Scala中定义异构映射。这是一个粗略的草图:
class HMap[A[_], B[_]] extends Iterable[HMap.Mapping[A, B, _]] {
private val self = mutable.Map[A[_], B[_]]()
def toMapping[T](a: A[_], b: B[_]): HMap.Mapping[A, B, T] = {
HMap.Mapping(a.asInstanceOf[A[T]], b.asInstanceOf[B[T]])
}
def iterator: Iterator[HMap.Mapping[A, B, _]] =
new Iterator[HMap.Mapping[A, B, _]] {
val sub = self.iterator
def hasNext = sub.hasNext
def next(): HMap.Mapping[A, B, _] = {
val (key, value) = sub.next()
toMapping(key, value)
}
}
def update[T](key: A[T], value: B[T]) = (self(key) = value)
def get[T](key: A[T]) = self.get(key).asInstanceOf[Option[B[T]]]
def apply[T](key: A[T]) = self(key).asInstanceOf[B[T]]
}
object HMap {
case class Mapping[A[_], B[_], T](val key: A[T], val value: B[T])
}
这可以通过内部使用链接的映射列表而不是映射来实现完全类型安全,但这对于性能更好。
我原来的例子如下:
object Example {
type Identity[T] = T
type Handler[T] = (T) => _
val handlers = new HMap[Identity, Handler]
def doSomething() {
for (HMap.Mapping(value, handler) <- handlers) {
handler(value)
}
}
}
这几乎是完美的,除了我不确定如何添加边界。
答案 1 :(得分:1)
您正在尝试描述一种higher-rank polymorphic异构映射,其中映射中的每个键值对都可以具有不同的类型参数。虽然很酷,Scala的类型系统不允许你静态表达。我认为你能做的最好的事情是定义一些可怕的,不安全的辅助方法:
def get [A] (map: Map[Foo[_], Bar[_]], k: Foo[A]) : Bar[A]
def put [A] (map: Map[Foo[_], Bar[_]], k: Foo[A], v: Bar[A])
您可以使用Manifest
在运行时更新每个键值对的类型参数,使其更加安全,但我不确定如何...
答案 2 :(得分:0)
我已经实现了一个可以满足您需求的地图。您可以在此处找到一些基本文档:https://github.com/sullivan-/emblem/wiki/TypeBoundMaps
TypeBoundMaps采用具有单个类型参数的类型,因此您需要在地图中为键和值类型引入几个新类型:
trait Parameter
type Identity[P <: Parameter] = P
type Handler[P <: Parameter] = (P) => _
现在您可以创建所需的地图:
var handlers = TypeBoundMap[Parameter, Identity, Handler]()
以下是使用地图的几个例子:
trait P1 extends Parameter
trait P2 extends Parameter
val p1: P1 = new P1 {}
val f1: Handler[P1] = { p1: P1 => () }
handlers += p1 -> f1 // add a new pair to the map
val f2: Handler[P1] = handlers(p1) // retrieve a value from the map
现在,为了模仿示例中的for循环,我们需要引入一个新类型TypeBoundPair
,它是一个键值对,其中参数值匹配:
def handle[P <: Parameter](pair: TypeBoundPair[Parameter, Identity, Handler, P]): Unit = {
pair._2(pair._1)
}
handlers.foreach { pair => handle(pair) }
介绍Identity
和Handler
类型背后的想法在此处详细说明:http://tinyurl.com/multi-tparam
答案 3 :(得分:-2)
scala> trait MyPair {
| type T
| val key:Foo[T]
| val value:Bar[T]
| }
defined trait MyPair
scala> var l:List[MyPair] = _
l: List[MyPair] = null
scala> l = List(new MyPair{type T = Int; val key = new Foo[Int]{}; val value = new Bar[Int]{} })
l: List[MyPair] = List($anon$1@176bf9e)
scala> l = List(new MyPair{type T = Int; val key = new Foo[Int]{}; val value = new Bar[Int]{} }, new MyPair {type T = String; val key = new Foo[String]{}; val value = new Bar[String]{} })
l: List[MyPair] = List($anon$1@d78fb4, $anon$4@1b72da)
scala> l = List(new MyPair{type T = Int; val key = new Foo[Int]{}; val value = new Bar[Int]{} }, new MyPair {type T = String; val key = new Foo[String]{}; val value = new Bar[Int]{} })
<console>:11: error: overriding value value in trait MyPair of type Bar[this.T];
value value has incompatible type
l = List(new MyPair{type T = Int; val key = new Foo[Int]{}; val value = new Bar[Int]{} }, new MyPair {type T = String; val key = new Foo[String]{}; val value = new Bar[Int]{} })