Относящиеся к параметризованным типам - PullRequest
8 голосов
/ 26 октября 2011

У меня есть карта, где ключи и значения являются общими типами. Примерно так:

Map[Foo[A], Bar[A]]

Что я хотел бы выразить, так это то, что тип A может отличаться для каждой пары ключ-значение на карте, но каждый ключ всегда параметризован тем же типом, что и значение, на которое он отображается. Таким образом, Foo[Int] всегда отображается на Bar[Int], Foo[String] всегда отображается на Bar[String] и т. Д.

Кто-нибудь знает способ выразить это?

EDIT:

Вот пример того, что я пытаюсь сделать:

trait Parameter // not important what it actually does

class Example {
  val handlers: Map[_ <: Parameter, (_ <: Parameter) => _] = Map()

  def doSomething() {
    for ((value, handler) <- handlers) {
      handler(value)
    }
  }
}

Идея состоит в том, что значение всегда будет отображаться в функцию, которая может принять его в качестве параметра, но поскольку код написан сейчас, компилятор не может этого знать.

Ответы [ 4 ]

2 голосов
/ 30 октября 2011

Как оказалось, в 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 голос
/ 26 октября 2011

Вы пытаетесь описать своего рода полиморфную более высокого ранга, неоднородную карту, где каждая пара ключ-значение на карте может иметь свой параметр типа. Как бы круто это ни было, система типов 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 s, чтобы повторно определить параметры типа каждой пары ключ-значение во время выполнения, но я не уверен, как ...

0 голосов
/ 10 октября 2015

Я реализовал карту, которая делает то, что вы хотите. Вы можете найти некоторые основные документы по этому вопросу здесь: 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

0 голосов
/ 26 октября 2011
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]{} })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...