Binding Parameterized Types

I have a map where both keys and values ​​are typical types. Something like that:

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

What I would like to express is that type A may differ for each key-value pair on the map, but each key is always parameterized with the same type as the value to which it is mapped. Thus, a Foo[Int] always maps to a Bar[Int] , a Foo[String] always maps to a Bar[String] , etc.

Does anyone know a way to express this?

EDIT:

Here is an example of what I'm trying to do:

 trait Parameter // not important what it actually does class Example { val handlers: Map[_ <: Parameter, (_ <: Parameter) => _] = Map() def doSomething() { for ((value, handler) <- handlers) { handler(value) } } } 

The idea is that the value will always be displayed in a function that can take it as a parameter, but the compiler cannot know this as the code is written.

+8
generics scala
source share
4 answers

As it turns out, Scala can define a heterogeneous map. Here is a rough sketch:

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

This can be done completely typical by using a linked map list instead of a map internally, but it is better for performance.

My original example would look like this:

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

This is almost perfect, but I'm not sure how to add borders.

+2
source share

You are trying to describe a kind of higher rank polymorphism , a heterogeneous map, where each pair of key values ​​on the map can have a different type of parameter. No matter how cool it is, a system like Scala does not allow you to express it statically. I think the best you can do is define some terrible, unsafe helper methods:

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

You may be able to make it somewhat more secure using Manifest to validate the type parameters of each key-value pair at runtime, but I'm not sure how ...

+1
source share

I implemented a map that does what you want. Here you can find the basic documentation: https://github.com/sullivan-/emblem/wiki/TypeBoundMaps

TypeBoundMaps uses types with one type of parameter, so you will need to enter a couple of new types for your keys and value types on the map:

 trait Parameter type Identity[P <: Parameter] = P type Handler[P <: Parameter] = (P) => _ 

Now you can create the map you need:

 var handlers = TypeBoundMap[Parameter, Identity, Handler]() 

Here are some examples of using a map:

 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 

Now, to emulate the for loop in your example, we need to introduce a new TypeBoundPair type, which is a key-value pair, where the parameter values ​​correspond to:

 def handle[P <: Parameter](pair: TypeBoundPair[Parameter, Identity, Handler, P]): Unit = { pair._2(pair._1) } handlers.foreach { pair => handle(pair) } 

The idea of ​​introducing Identity and Handler types is explained in more detail here: http://tinyurl.com/multi-tparam

0
source share
 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]{} }) 
-2
source share

All Articles