Why does Scala not infer type parameters when pattern matching with @

I am using Scala 2.10.4 with akka 2.3.4. I am facing an issue where type inference does not behave as I expected.

Below is an example of what I am experiencing. I have a case class that wraps messages with a id

named MyMessage

. It is parameterized by the message type. Then I have a named payload MyPayload

that contains String

.

Inside the actor (I'm just using a regular named object here MyObject

, since the problem is not specific to akka). I am matching templates and calling a function that works on my payload type MyPayload

.

package so

case class MyMessage[T](id:Long, payload:T)
case class MyPayload(s:String)

object MyObject {
  def receive:PartialFunction[Any, Unit] = {
    case m @ MyMessage(id, MyPayload(s)) =>

      // Doesn't compile
      processPayload(m)

      // Compiles
      processPayload(MyMessage(id, MyPayload(s)))
  }

  def processPayload(m:MyMessage[MyPayload]) = {
    println(m)
  }
}

      

For reasons I don't understand, pattern patterning with @

and the inapplicable case class does not infer the type parameter MyMessage[T]

. In the above code, I would assume it m

will be of type MyMessage[MyPayload]

. However, when I compile, it thinks that the type MyMessage[Any]

.

[error] PatternMatch.scala:9: type mismatch;
[error]  found   : so.MyMessage[Any]
[error]  required: so.MyMessage[so.MyPayload]
[error] Note: Any >: so.MyPayload, but class MyMessage is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error]       processPayload(m)
[error]                      ^
[error] one error found
[error] (compile:compile) Compilation failed
[error] Total time: 1 s, completed Aug 19, 2014 12:08:04 PM

      

Is this the expected behavior? If so, what have I misunderstood about the type of inference in Scala?

+3


source to share


3 answers


You cannot retrieve type parameters in pattern matching - this is a limitation of the current implementation and / or runtime. Since type parameters are erased at runtime, there is a lot of overhead to recover them, so you cannot use a method unapply

that accepts a type parameter in pattern matching.

It looks simpler in your case, because the compiler can simply infer the type from the extractor arguments. But in general it is not that simple and is probably the reason why it does not work even in your case.



See this long live ticket about the problem.

+4


source


The problem you are facing is erasing styles

JVM knows nothing about typical types at runtime, see

How to get around style erasure in Scala? Or why can't I get the type parameter of my collections?

To compile it you have to tell the generic what type you expect



 def receive:PartialFunction[Any, Unit] = {
    case message: MyMessage[MyPayload] =>
      processPayload(message)
  }

      

Warning: this will still match any MyMessage [_] and may throw runtime exceptions .

To provide a type at runtime you need to use TypeTags (see link above)

+2


source


Funnily enough, it also works for:

def receive: PartialFunction[Any, Unit] = {
 case m : MyMessage[MyPayload] => processPayload(m)
}

scala> MyObject.receive.isDefinedAt(MyMessage(12L, MyPayload("string")))
res9: Boolean = true

      

0


source







All Articles