2012-01-09 133 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 編譯器會檢查我們的模式匹配詳盡,並會 抱怨,如果某些組合的可能值不包括在 模式。沒有這樣的全面性檢查,可用於提取。」

這對我說,兩者的區別並沒有人們所期望的第一眼,但沒有被具體確切的區別是什麼。