Почему Scala не выводит параметры типа при сопоставлении с шаблоном @


Я использую Scala 2.10.4 с akka 2.3.4. Я столкнулся с проблемой, когда вывод типа ведет себя не так, как я ожидал.

Приведенный ниже код иллюстрирует пример того, что я испытываю. У меня есть класс case, который обертывает сообщения с именем id MyMessage. Он параметризуется типом сообщения. Тогда у меня есть полезная нагрузка с именем MyPayload, которая содержит String.

Внутри актора (здесь я просто использую обычный объект с именем MyObject, поскольку проблема не в частности, для akka) я сопоставляю шаблон и вызываю функцию, которая работает с моим типом полезной нагрузки 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)
  }
}

По причинам, которых я не понимаю, паттерн patching с @ и неприменимым классом case не выводит параметр типа MyMessage[T]. В приведенном выше коде я ожидал, что m будет иметь тип MyMessage[MyPayload]. Однако, когда я компилирую, он считает, что тип 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

Является ли это ожидаемым поведением? Если да, то что я неправильно понял о типе вывод в скале?

3 3

3 ответа:

Вы не можете извлечь параметры типа в сопоставлении с шаблоном - это ограничение текущей реализации и / или среды выполнения. Поскольку параметры типа стираются во время выполнения, для их восстановления потребуется много накладных расходов , поэтому нельзя использовать метод unapply, который принимает параметр типа в соответствии с шаблоном.

В вашем случае это выглядит проще, потому что компилятор может просто вывести тип из аргументов экстрактора. Но в целом это не так просто и, вероятно, причина почему это даже не работает в вашем случае.

Смотритеэтот билет на долгую жизнь о проблеме.

Проблема, с которой вы столкнулись, - это стирание типа

JVM ничего не знает об универсальных типах во время выполнения, см.:

Как обойти стирание типа на Scala? Или, почему я не могу получить параметр типа моих коллекций?

Чтобы заставить его компилироваться, вы должны сказать компилятору эксплицитно, какой тип вы ожидаете

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

Предупреждение: это все равно будет соответствовать любому MyMessage [_] и может вызвать исключения времени выполнения.

Обеспечить тип во время выполнения, вам нужно использовать TypeTags (см. ссылку выше)

Как ни странно, это также работает для:

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

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