简体   繁体   English

Scala:案例类不适用与手动实现和类型擦除

[英]Scala: Case class unapply vs a manual implementation and type erasure

I'm trying to understand what Scala does with Case Classes that makes them somehow immune to type erasure warnings.我试图了解 Scala 对 Case Class 做了什么,使它们在某种程度上不受类型擦除警告的影响。

Let's say we have the following, simple class structure.假设我们有以下简单的类结构。 It's basically an Either :它基本上是一个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]

And you're trying to use it like this:而你正试图像这样使用它:

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

Everything compiles and runs without any problems.一切都编译和运行没有任何问题。 However, when I try implementing the unapply method myself, the compiler throws a warning.但是,当我尝试自己实现unapply方法时,编译器会抛出警告。 I used the following class structure with the same Main class above:我在上面的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]

Compiling that with the -unchecked flag issues the following warning:使用-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

Now, I understand type erasure and I've tried to get around the warning with Manifests (to no avail so far), but what is the difference between the two implementations?现在,我了解类型擦除,并且我已经尝试使用Manifests来解决警告(目前无济于事),但是这两种实现之间有什么区别? Are case classes doing something that I need to add in?案例类是否在做一些我需要添加的事情? Can this be circumvented with Manifests ?这可以通过Manifests规避吗?

I even tried running the case class implementation through the scala compiler with the -Xprint:typer flag turned on, but the unapply method looks pretty much like I expected:我甚至尝试运行通过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);

I cannot give a complete answer, but I can tell you that even though the compiler generates an unapply method for case classes, when it pattern matches on a case class it does not use that unapply method.我无法给出完整的答案,但我可以告诉您,即使编译器为 case 类生成了unapply方法,当它在 case 类上进行模式匹配时,它也不会使用该 unapply 方法。 If you try -Ybrowse:typer using both builtin case matching and your unapply method, you will see a very different syntax tree is produced (for the match ) depending on which is used.如果您尝试-Ybrowse:typer使用内置大小写匹配和unapply方法,您将看到生成的语法树(对于match )非常不同,具体取决于使用的语法树。 You can also browse the later phases and see that the difference remains.您还可以浏览后面的阶段并查看差异仍然存在。

Why Scala does not use the builtin unapply I am not sure, though it may be for the reason you bring up.为什么 Scala 不使用内置 unapply 我不确定,虽然这可能是你提出的原因。 And how to get around it for your own unapply I have no idea.而如何避开它为自己的unapply ,我不知道。 But this is the reason Scala seems to magically avoid the problem.但这就是 Scala 似乎神奇地避免了这个问题的原因。

After experimenting, apparently this version of unapply works, though I'm a bit confused about why:经过试验,显然这个版本的unapply有效,但我对为什么有点困惑:

def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match {
    case w: White[_,_] => Some(w.right)
    case _ => None
}

The difficulty with your unapply is that somehow the compiler has to be convinced that if a White[A,B] extends a BlackOrWhite[C,D] then B is the same as D , which apparently the compiler is able to figure out in this version but not in yours.unapply的困难在于,编译器必须以某种方式确信如果White[A,B]扩展了BlackOrWhite[C,D]那么BD相同,显然编译器能够在这个版本但不是你的。 Not sure why.不知道为什么。

I can't give you the answer on the difference between case class match and unapply.我不能给你关于 case class match 和 unapply 之间区别的答案。 However in their book (Odersky, Spoon, Venners) "Programming in Scala" 2nd chptr 26.6 "Extractors versus case classes" they write:然而,在他们的书(Odersky、Spoon、Venners)“Scala 编程”第 2 章 26.6“提取器与案例类”中,他们写道:

"they (case classes) usually lead to more efficient pattern matches than extractors, because the Scala compiler can optimize patterns over case classes much better than patterns over extractors. This is because the mechanisms of case classes are fixed, whereas an unapply or unapplySeq method in an extractor could do almost anything. Third, if your case classes inherit from a sealed base class, the Scala compiler will check our pattern matches for exhaustiveness and will complain if some combination of possible values is not covered by a pattern. No such exhaustiveness checks are available for extractors." “它们(案例类)通常导致比提取器更有效的模式匹配,因为 Scala 编译器可以比提取器上的模式更好地优化案例类上的模式。这是因为案例类的机制是固定的,而 unapply 或 unapplySeq 方法在提取器中几乎可以做任何事情。第三,如果您的案例类继承自密封基类,Scala 编译器将检查我们的模式匹配是否详尽,并且如果某个模式未涵盖某些可能值的组合,则会抱怨。没有这种详尽程度提取器可以使用支票。”

Which says to me that the two are more different than one would expect at first glance, however without being specific on what the exact differences are.这对我说,乍一看,这两者的不同比人们预期的要大,但没有具体说明确切的区别是什么。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM