Разница между самодельным экстрактором и экстрактором корпусного класса

Согласно спецификации scala, экстрактор, созданный классами case, выглядит следующим образом (спецификация scala §5.3.2):

def unapply[tps](x: c[tps]) =
  if (x eq null) scala.None
  else scala.Some(x.xs11, ..., x.xs1k)

По причинам реализации я хочу иметь возможность имитировать поведение этого экстрактора в классе без регистра. Однако моя реализация не может воспроизвести такое же поведение.

Вот пример разницы, которая у меня есть:

trait A

sealed trait B[X <: A]{ val x: X }

case class C[X <: A](x: X) extends B[X]

class D[X <: A](val x: X) extends B[X]

object D {
  def unapply[X <: A](d: D[X]): Option[X] =
    if (d eq None) None
    else Some(d.x)
}

def ext[X <: A](b: B[X]) = b match {
  case C(x) => Some(x)
  case D(x) => Some(x)
  case _ => None
}

У меня есть следующее предупреждение:

<console>:37: warning: non variable type-argument X in type pattern D[X] is unchecked since it is eliminated by erasure
     case D(x) => Some(x)

Обратите внимание, что предупреждение появляется только в случае D, а не в случае textractor case-class. Есть ли у вас какие-либо идеи о причине предупреждения / о том, что я должен сделать, чтобы избежать этого предупреждения?

Примечание. Если вы хотите протестировать его в REPL, самый простой способ:

  1. Чтобы активировать непроверенное предупреждение

    скала> :сила

    scala> settings.unchecked.value = true

  2. Чтобы скопировать приведенный выше код в режиме вставки:

    скала> :вставить

    [копировать вставить]

    [Ctrl + D]

Редактировать: Как упомянул Анторас, это должна быть ошибка компилятора, возможно, версия scala может быть полезна: scala 2.9.0.1 (после быстрого теста, все еще в scala 2.9.1RC2)


person Nicolas    schedule 10.08.2011    source источник


Ответы (1)


Кажется, это ошибка компилятора. Я проанализировал вывод компилятора AST (с fsc -Xprint:typer <name_of_file>.scala). Он интерпретирует оба как одно и то же:

...
    final <synthetic> object C extends java.lang.Object with ScalaObject with Serializable {
      def this(): object test.Test.C = {
        C.super.this();
        ()
      };
      final override def toString(): java.lang.String = "C";
      case <synthetic> def unapply[X >: Nothing <: test.Test.A](x$0: test.Test.C[X]): Option[X] = if (x$0.==(null))
        scala.this.None
      else
        scala.Some.apply[X](x$0.x);
      case <synthetic> def apply[X >: Nothing <: test.Test.A](x: X): test.Test.C[X] = new test.Test.C[X](x);
      protected def readResolve(): java.lang.Object = Test.this.C
    };
...
    final object D extends java.lang.Object with ScalaObject {
      def this(): object test.Test.D = {
        D.super.this();
        ()
      };
      def unapply[X >: Nothing <: test.Test.A](d: test.Test.D[X]): Option[X] = if (d.eq(null))
        scala.None
      else
        scala.Some.apply[X](d.x)
    };
...

Сигнатура обоих методов unapply идентична.

Кроме того, код работает нормально (как и ожидалось, благодаря идентичным методам):

trait A {
  def m = "hello"
}

class AA extends A

sealed trait B[X <: A]{ val x: X }

case class C[X <: A](x: X) extends B[X]

class D[X <: A](val x: X) extends B[X]

object D {
  def apply[X <: A](x: X) = new D(x)
  def unapply[X <: A](d: D[X]): Option[X] =
    if (d eq null) None
    else Some(d.x)
}

def ext[X <: A](b: B[X]) = b match {
  case C(x) => Some("c:"+x.m)
  case D(x) => Some("d:"+x.m)
  case _ => None
}
println(ext(C[AA](new AA())))
println(ext(D[AA](new AA())))
person kiritsuku    schedule 10.08.2011
comment
Как вы думаете, у нас достаточно материала, чтобы открыть тикет? - person Nicolas; 10.08.2011
comment
Да, я думаю, этого достаточно. Есть предупреждение, но тем не менее код работает нормально. Кроме того, сгенерированный код также работает нормально и идентичен. - person kiritsuku; 11.08.2011