【问题标题】:Relating parameterized types关联参数化类型
【发布时间】:2011-10-26 00:20:28
【问题描述】:

我有一个键和值都是泛型类型的映射。像这样的:

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)
    }
  }
}

这个想法是,一个值总是会映射到一个可以接受它作为参数的函数,但是由于现在编写代码,编译器无法知道这一点。

【问题讨论】:

  • 该行无法自行编译,scala 需要从某个地方选择这些类型(类或方法类型注释,或抽象类型),您能给我们看一下代码吗?

标签: generics scala


【解决方案1】:

事实证明,在 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)
    }
  }
}

这几乎是完美的,除了我不确定如何添加边界。

【讨论】:

    【解决方案2】:

    您试图描述一种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])
    

    您可以使用Manifests 在运行时具体化每个键值对的类型参数,使其更加安全,但我不确定如何...

    【讨论】:

    • 可以说它们应该是封装不安全映射的对象中的方法——然后用户将获得一个更安全的接口(甚至是一个安全的接口,如果条目只能通过该接口插入;但这通常是太严格了)。基本接口不需要清单。实现当然需要使用强制转换,但您可以确保它们不会失败。
    【解决方案3】:

    我已经实现了一张你想要的地图。你可以在这里找到一些基本的文档: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) }
    

    在此处更详细地解释了引入 IdentityHandler 类型背后的想法:http://tinyurl.com/multi-tparam

    【讨论】:

      【解决方案4】:
      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]{} })
      

      【讨论】:

        猜你喜欢
        • 2016-11-06
        • 1970-01-01
        • 1970-01-01
        • 2019-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多