Scala: класс Case неприменим против реализации вручную и удаления типа

Я пытаюсь понять, что делает Scala с классами дел, что делает их каким-то образом невосприимчивыми к предупреждениям об удалении типов.

Допустим, у нас есть следующая простая структура классов. Это в основном Either:

abstract class BlackOrWhite[A, B]

case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]

case class White[A,B]( val right: B ) extends BlackOrWhite[A,B]

И вы пытаетесь использовать это так:

object Main extends App {

    def echo[A,B] ( input: BlackOrWhite[A,B] ) = input match {
        case Black(left) => println( "Black: " + left )
        case White(right) => println( "White: " + right )
    }

    echo( Black[String, Int]( "String!" ) )
    echo( White[String, Int]( 1234 ) )
}

Все компилируется и работает без проблем. Тем не менее, когда я пытаюсь реализовать unapply сам метод, компилятор выдает предупреждение. Я использовал следующую структуру класса с тем же Main класс выше:

abstract class BlackOrWhite[A, B]

case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]

object White {

    def apply[A,B]( right: B ): White[A,B] = new White[A,B](right)

    def unapply[B]( value: White[_,B] ): Option[B] = Some( value.right )

}

class White[A,B]( val right: B ) extends BlackOrWhite[A,B]

Компилируя это с -unchecked flag выдает следующее предупреждение:

[info] Compiling 1 Scala source to target/scala-2.9.1.final/classes...
[warn] src/main/scala/Test.scala:41: non variable type-argument B in type pattern main.scala.White[_, B] is unchecked since it is eliminated by erasure
[warn]         case White(right) => println( "White: " + right )
[warn]                   ^
[warn] one warning found
[info] Running main.scala.Main

Теперь я понимаю стирание типа и попытался обойти предупреждение Manifests (пока безрезультатно), но в чем разница между двумя реализациями? Классы дел делают то, что мне нужно добавить? Можно ли обойти это с Manifests?

Я даже пытался запустить реализацию класса case через компилятор scala с -Xprint:typer флаг включен, но unapply Метод выглядит так, как я ожидал:

case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x$0: $iw.$iw.White[A,B]): Option[B] = if (x$0.==(null))
    scala.this.None
else
    scala.Some.apply[B](x$0.right);

заранее спасибо

2 ответа

Решение

Я не могу дать полный ответ, но могу вам сказать, что даже если компилятор генерирует unapply метод для case-классов, когда он соответствует шаблону case-класса, он не использует этот неприменимый метод. Если вы попытаетесь -Ybrowse:typer используя как встроенный регистр соответствия и ваш unapply метод, вы увидите совершенно другое дерево синтаксиса (для match) в зависимости от того, какой используется. Вы также можете просмотреть более поздние этапы и увидеть, что разница остается.

Почему Scala не использует встроенное приложение неприменимо, я не уверен, хотя это может быть по той причине, что вы подняли. И как обойти это самостоятельно unapply Я понятия не имею. Но это причина, по которой Скала волшебным образом избегает этой проблемы.

После экспериментов, видимо, эта версия unapply работает, хотя я немного запутался, почему:

def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match {
    case w: White[_,_] => Some(w.right)
    case _ => None
}

Сложность с вашим unapply является то, что каким-то образом компилятор должен быть убежден, что если White[A,B] расширяет BlackOrWhite[C,D] затем B такой же как DЧто, видимо, компилятор может выяснить в этой версии, но не в вашей. Не уверен почему.

Я не могу дать вам ответ о разнице между совпадением классов дел и неприменением. Однако в своей книге ("Одерский, Ложка, Веннерс") "Программирование в Scala", 2-й глава 26.6 "Экстракторы и классы падежей", они пишут:

"они (case-классы) обычно приводят к более эффективному сопоставлению с образцом, чем к экстракторам, потому что компилятор Scala может оптимизировать шаблоны по классам case гораздо лучше, чем шаблоны по экстракторам. Это потому, что механизмы классов case являются фиксированными, тогда как метод unapply или unapplySeq в экстракторе может сделать почти все. В-третьих, если ваши классы прецедентов наследуются от запечатанного базового класса, компилятор Scala проверит наши совпадения с образцом на предмет исчерпываемости и будет выдавать жалобу, если какая-либо комбинация возможных значений не охвачена шаблоном. чеки доступны для экстракторов."

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

Другие вопросы по тегам