Search code examples
scalaexistential-type

Scala: existential types for a Map


I want to use a map of varying types on an unknown A:

val map: Map[Foo[A], Bar[A]] = ...
...
val foo = new Foo[Qux]
val bar: Bar[Qux] = map(foo)

This doesn't work, because A is an unknown. I have to define it instead as:

val map: Map[Foo[_], Bar[_]] = ...
...
val foo = new Foo[Qux]
val bar: Bar[Qux] = map(foo).asInstanceOf[Bar[Qux]]

This works, but the cast is ugly. I'd rather find a better way. I gather the answer is to use existential types with the forSome keyword, but I'm confused as to how that works. Should it be:

Map[Foo[A], Bar[A]] forSome { type A }

or:

Map[Foo[A] forSome { type A }, Bar[A]]

or:

Map[Foo[A forSome { type A }], Bar[A]]

Solution

  • Actually, none of these work.

    Map[Foo[A], Bar[A]] forSome { type A }
    

    is a Map where all keys are of the same type Foo[A] and values of type Bar[A] (but the type A may be different for different maps of this type); in second and third examples, A in Bar[A] is completely different from A under forSome.

    This ugly workaround should work:

    // 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[_]] = ...