2012-01-09 135 views
18

我想了解什么是斯卡拉与案例类,使他们以某种方式免疫键入擦除警告。斯卡拉:案例类不适用与手动执行和类型擦除

假设我们有以下简单的类结构。这基本上是一个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(目前为止无效)的警告,但这两种实现有什么区别?案例类是否需要添加一些东西?这可以用Manifests绕过吗?

我甚至试图通过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); 

感谢

+0

你使用最新版本的Scala?我无法重现您的问题,并且几个月前的这个相关问题确定了与您的类似问题,因为它是编译器错误。请参阅http://stackoverflow.com/questions/7008428/difference在自制的提取器和案例类提取器中 – Destin 2012-01-09 04:03:02

+0

我使用的是2.9.1.final(在Xubuntu 11.10上,如果它很重要) – Nycto 2012-01-09 16:40:15

回答

12

我不能给出一个完整的答案,但我可以告诉你,尽管编译器为case类生成了一个unapply方法,但是当它在一个case类上匹配时,它不会使用该unapply方法。如果您尝试使用-Ybrowse:typer同时使用内置大小写匹配和unapply方法,则会看到生成了非常不同的语法树(对于match),具体取决于使用的是哪一种。您也可以浏览更新的阶段,并看到差异仍然存在。

为什么Scala不使用内置的无法应用我不确定,虽然它可能是你提出的原因。以及如何避免你自己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]扩展了一个BlackOrWhite[C,D]然后B是与D相同,这显然编译器能够在这个版本中找出,但不是在你的。不知道为什么。

+2

实际上,创建'unapply'是为了让用户可以复制功能那个'case class' pr ovided。还有其他一些地方,官方对事物如何工作的解释并不是编译器实际做到的。 – 2012-01-09 13:34:53

5

我不能给你关于case class match和unapply之间区别的答案。然而在他们的书中(Odersky,Spoon,Venners)“Programming in Scala”第二集26。6“提取器相对于壳体的类”他们写:

“他们(case类),通常导致更有效的模式匹配 比提取器,因为Scala编译器可以优化在 case类模式不是通过提取器图案好得多。这是 ,因为case类的机制是固定的,而在抽取器中不应用 或unapplySeq方法几乎可以做任何事情。第三,如果你的case类继承自密封基类,Scala 编译器会检查我们的模式匹配详尽,并会 抱怨,如果某些组合的可能值不包括在 模式。没有这样的全面性检查,可用于提取。”

这对我说,两者的区别并没有人们所期望的第一眼,但没有被具体确切的区别是什么。