Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between home made extractor and case class extractor

According to the scala specification, the extractor built by case classes is the following (scala specification §5.3.2):

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

For implementation reasons, I want to be able to mimic the behavior of this extractor on a non-case class. However, my implementation fails to reproduce the same behavior.

Here is an example of the difference i have:

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
}

I have the following warning :

<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)

Notice the warning occurs only in the D case, not in the case-class textractor case. Do you have any idea about the cause of the warning / about what I should do to avoid this warning ?

Note: If you want to test it in REPL, the easiest way is:

  1. To activate unchecked warning

    scala> :power

    scala> settings.unchecked.value = true

  2. To copy above code in paste mode:

    scala> :paste

    [copy/paste]

    [ctrl + D]

Edit: As Antoras mentioned it should be a compiler bug, maybe the scala version could be useful: scala 2.9.0.1 (after a quick test, still there in scala 2.9.1RC2)

like image 551
Nicolas Avatar asked Aug 10 '11 09:08

Nicolas


1 Answers

This seems to be a compiler bug. I have analyzed the output of the compiler AST (with fsc -Xprint:typer <name_of_file>.scala). It interprets both as the same:

...
    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)
    };
...

The method signature of both methods unapply are identical.

Furthermore the code works fine (as expected due to identical methods):

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())))
like image 116
kiritsuku Avatar answered Sep 30 '22 15:09

kiritsuku