Странное поведение при сопоставлении шаблонов с AnyRef

#scala #pattern-matching

#scala #сопоставление с шаблоном

Вопрос:

 def test1(a: Any) = a match {
  case x: AnyRef => "AnyRef"
  case _ => "None of the above"
}

def test2(a: Any) = a match {
  case x: Double if x > 2 => "Double > 2"
  case x: AnyRef => "AnyRef"
  case _ => "None of the above"
}
  

Пожалуйста, кто-нибудь может объяснить, почему в приведенном ниже первом случае 1.0 совпадение включено AnyRef , а во втором — нет. (Scala 2.9.0-1)

 scala> test1(1.0)
res28: java.lang.String = AnyRef

scala> test2(1.0)
res29: java.lang.String = None of the above
  

редактировать — Обновление Scala 2.10 от января 2013: новый механизм сопоставления шаблонов исправляет это поведение (или, по крайней мере, делает его согласованным), и метод test2 теперь возвращает «AnyRef» как для test1 .

Комментарии:

1. при компиляции генерируется if(экземпляр Double) { if(a > 2) { возвращает «Double > 2»} else { возвращает «Ничего из вышеперечисленного» } } else { if (экземпляр объекта) { возвращает «AnyRef»} else {возвращает «Ничего из вышеперечисленного»}} . Итак, если кто-нибудь не найдет что-нибудь в спецификации scala, я думаю, что это ошибка компилятора scala

2. 1.0 является a, Double который является подтипом Any , но не of AnyRef в отличие от java.lang.Double . Поэтому я даже удивляюсь, почему 1.0 совпадения AnyRef в test1 .

3. Я удалил свой ответ. Это не ошибка — я не заметил, чтобы вы сопоставляли с AnyRef . Рекс Керр прав.

4. @PeterSchmitz: согласно спецификации, вы правы.

Ответ №1:

Это потому, что Any на самом деле это просто Object . Поскольку Double существует удобная фикция — на самом деле это java.lang.Double то, что автоматически устанавливается для вас в инструкции match. К сожалению, у Scala нет способа определить, находит ли она java.lang.Double , должно ли это интерпретироваться как Double или как a java.lang.Double — в последнем случае AnyRef это должно быть перехвачено. Так и есть. Но если вы специально запрашиваете a Double , он знает, что он должен быть распакован, и тогда AnyRef регистр проверять не нужно. (И, на самом деле, если вы предполагали, что это будет java.lang.Double , это тоже будет распаковано — он не сможет определить разницу.)

Является ли это идеальным поведением, спорно, но оно логично.

Комментарии:

1. Спасибо, теперь я понимаю, почему 1.0 это сопоставляется в test1 .

2. @RexKerr Я не уверен, что все еще верю в это. «в последнем случае AnyRef должен это перехватить». за исключением того, что этого не происходит, поскольку «Ничего из вышеперечисленного» не напечатано. Я также предполагаю, что результат сопоставления должен быть равен составлению частичных функций… val x:PartialFunction[Any, String] = {case x:Double if x > 2 => "Double > 2"} val y:PartialFunction[Any, String] = {case x:AnyRef => "AnyRef"} val z:PartialFunction[Any, String] = {case _ => "None of the Above"} println(test2(1.0)) // => None of the Above val chain = x orElse y orElse z println(chain(1.0)) // => AnyRef

3.@DanielHinojosa — Как я уже сказал, я не уверен, что это идеально: AnyRef должно перехватывать это , если вы не укажете, что хотите Double . Можно утверждать, что защита на Double должна указывать, что вы хотите, чтобы только те Double , которые будут рассматриваться как не- AnyRef , а остальные должны быть перехвачены AnyRef как обычно.

4. Описано ли это поведение в спецификации Scala? Мне действительно интересно, как нынешнее поведение может быть формализовано в целом.

5. @RexKerr: Ну, это не будет распространяться ни на AnyVal дочерние элементы, ни на перестановки разных случаев. В любом случае, я просмотрел спецификацию, и ни в разделах 12.2, ни в разделах 8.1-8.2 нет намека на какую-либо подобную проблему, поэтому первый фрагмент кода не согласуется со спецификацией. Но я боюсь, что настанет день, когда сопоставление с AnyRef будет работать и совпадения с AnyVal будут отфильтровываться, учитывая стоимость производительности. Я думаю, что совпадения с AnyRef должны вызывать предупреждение или ошибку, предлагая сопоставить с Any , тем более что AnyVal это уже запрещено.