score:2

Accepted answer

tl;dr Type erasure and pattern matching

Typing the Map with SuperTrait concealed information about the type, and caused the pattern matching to assume a broad type for your extractor.


This is a similar example, but using Any instead of your SuperTrait. This example also shows how to produce a runtime exception out of it.

case class Identity[A : Manifest]() {
  def apply(a: A) = a match { case a: A => a } // seemingly safe no-op
}

val myIdentity: Any = Identity[Int]()

myIdentity match {
  case f@Identity() => f("string") // uh-oh, passed String instead of Int
}

throws an exception

scala.MatchError: string (of class java.lang.String)
  at Identity.apply(...)

f@Identity() pattern matches the Any as an Identity[Any], and due to type erasure, this matched the Identity[Int], which turned into the error.


In constrast if we change Any to Identity[_],

case class Identity[A : Manifest]() {
  def apply(a: A) = a match { case a: A => a }
}

val myIdentity: Identity[_] = Identity[Int]()

myIdentity match {
  case f@Identity() => f("string")
}

correctly fails to compile.

found   : String("string")
required: _$1 where type _$1
     case f@Identity() => f("string")

It knows that f is the existential type Identity[T] forSome {type T}, and it can't show that String conforms to the wildcard type T.


In the first example, you were effectively pattern matching as

DesiredKeyConstraints.KeyConstraint[Any](_, constraint)

In the second, there was more information, and you were matching as

DesiredKeyConstraints.KeyConstraint[T](_, constraint) forSome {type T}

(This is just illustrative; you currently can't actually write type parameters when pattern matching.)


Related Query

More Query from same tag