Scala: класс Case unapply против ручной реализации и стирания типа
Я пытаюсь понять, что Scala делает с классами Case, что делает их как-то невосприимчивыми к типам предупреждений об удалении.
предположим, у нас есть следующая, простая структура классов. Это в основном 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
флаг выдает следующее предупреждение:
[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
(пока безрезультатно), но в чем разница между двумя реализациями? Классы case делают что-то, что мне нужно добавить? Можно ли обойти это с помощью Manifests
?
Я даже пытался запустить класс case реализация через компилятор scala с помощью -Xprint:typer
флаг включен, но unapply
метод выглядит примерно так, как я ожидал:
case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x: $iw.$iw.White[A,B]): Option[B] = if (x.==(null))
scala.this.None
else
scala.Some.apply[B](x.right);
спасибо заранее
2 ответов
я не могу дать полный ответ, но я могу сказать вам, что хотя компилятор генерирует unapply
метод для классов case, когда он соответствует шаблону в классе case, он не использует этот неприменимый метод. Если вы попробуете -Ybrowse:typer
используя как встроенный случай соответствия и ваш unapply
метод, вы увидите, что создается совсем другое синтаксическое дерево (для match
) в зависимости от того, который используется. Вы также можете просмотреть более поздние фазы и увидеть, что разница остается.
почему Scala не использует встроенный unapply я не уверен, хотя это может быть по причине, которую вы поднимаете. И как обойти это для себя unapply
понятия не имею. Но именно по этой причине Scala, похоже, волшебным образом избегает проблемы.
после экспериментов, видимо эта версия 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]
расширяет a BlackOrWhite[C,D]
затем B
это то же самое, что D
, который, по-видимому, компилятор может выяснить в этой версии, но не в вашей. Не знаю почему.
Я не могу дать вам ответ на разницу между совпадением классов case и unapply. Однако в своей книге (Odersky, Spoon, Venners)" программирование в Scala "2-й chptr 26.6" экстракторы против классов case " они пишут:
" Они (классы case) обычно приводят к более эффективным совпадениям шаблонов чем экстракторы, потому что компилятор Scala может оптимизировать модель классы case намного лучше, чем шаблоны над экстракторами. Это потому что механизмы дела классы фиксированы, в то время как unapply или метод unapplySeq в экстракторе может сделать почти все. Третий, если классы case наследуются от запечатанного базового класса, Scala компилятор проверит наши соответствия шаблонов на исчерпываемость и будет пожаловаться, если некоторая комбинация возможных значений не покрывается a узор. Для экстракторов такие проверки на исчерпывающую способность отсутствуют."
Что говорит мне, что эти два более разные, чем можно было бы ожидать сначала взгляд, однако, не будучи конкретным о том, каковы точные различия.