此代码编译:
def wtf(arg: Any) = {
arg match {
case Nil => "Nil was passed to arg"
case List() => "List() was passed to arg"
case _ =>"otherwise"
}
}
但是这个没有:
def wtf(arg: Any) = {
arg match {
case List() => "List() was passed to arg"
case Nil => "Nil was passed to arg"
case _ =>"otherwise"
}
}
行案例Nil => ... 被标记为无法访问的代码。为什么,在第一种情况下,行 case List()=> ... 没有标记相同的错误?
答案 0 :(得分:9)
实际答案需要了解一个不幸的实施细节,这花费了我很多时间才能发现。
1)case List()调用一个提取器,在一般情况下不能进行穷举/不可达性检查,因为提取器调用任意函数。到目前为止,我们不能指望我们推翻停止问题。
2)回到编译器的更多“狂野西部”时代,如果“case List()”刚刚被翻译成模块匹配,那么确定模式匹配可以加速相当多(并且不会失去穷举性检查)在早期的编译阶段,“case Nil”会避免提取器。情况仍然如此,虽然它可以撤消,显然很多人被告知“case List()=>”非常好,我们不想突然对所有代码感到悲观。所以我只需要找出一条出路。
您可以通过在其他类中尝试使用经验来查看List是否具有特权。没有不可达性错误。
import scala.collection.immutable.IndexedSeq
val Empty: IndexedSeq[Nothing] = IndexedSeq()
def wtf1(arg: Any) = {
arg match {
case Empty => "Nil was passed to arg"
case IndexedSeq() => "IndexedSeq() was passed to arg"
case _ =>"otherwise"
}
}
def wtf2(arg: Any) = {
arg match {
case IndexedSeq() => "IndexedSeq() was passed to arg"
case Empty => "Nil was passed to arg"
case _ =>"otherwise"
}
}
答案 1 :(得分:6)
这种差异特别奇怪,因为第二版中Nil
案例的代码肯定不无法访问,因为我们可以看到我们是否从编译器中隐藏了一些内容: / p>
def wtf(arg: Any) = {
arg match {
case List() => "List() was passed to arg"
case x => x match {
case Nil => "Nil was passed to arg"
case _ =>"otherwise"
}
}
}
现在wtf(Vector())
将返回"Nil was passed to arg"
。这也可能看似违反直觉,但这是因为文字模式与==
和Vector() == Nil
相等的值匹配,但Vector()
与提取器模式List()
不匹配
更简洁:
scala> (Vector(): Seq[_]) match { case List() => true; case Nil => false }
<console>:8: error: unreachable code
scala> (Vector(): Seq[_]) match { case List() => true; case x => x match { case Nil => false } }
res0: Boolean = false
所以编译器的响应完全颠倒了:在“好”版本中第二种情况是无法访问的,而在“坏”版本中第二种情况完全正常。我reported this as a bug (SI-5029)。
答案 2 :(得分:5)
Nil
是一个扩展List[Nothing]
的对象。比List()
更具体,如果它出现在案例表达式中的List()
之后,则无法达到。
虽然我认为上述情况或多或少都是正确的,但可能不是整个故事。
文章Matching Objects with Patterns中有一些提示,但我没有看到明确的答案。
我怀疑对于命名常量和文字而言,对于不可达性的检测比对构造函数模式更完全实现,并且List()
被解释为构造函数模式(即使它是一个微不足道的模式),而{{{ 1}}是一个命名常量。
答案 3 :(得分:3)
关于无法访问的匹配子句,我在语言规范中找不到任何内容。如果我错了,有人会纠正我。
所以我假设无法达到的编译错误是尽力而为的,这可以解释为什么第一个案例不会抱怨。
scala -Xprint:typer
建议Nil
是文字模式,使用immutable.this.Nil.==
来检查匹配,而List()
是提取器图案。查看提取器的实现似乎正在执行this:
def unapplySeq[A](x: CC[A]): Some[CC[A]] = Some(x)
所以我很好奇并推出了另一种实现方式:
object ListAlt {
def unapplySeq[A](l: List[A]): Some[List[A]] = Some(l)
}
匹配List()
:
scala> Nil match { case ListAlt() => 1 }
res0: Int = 1
但是如果我用它实现你的功能,它编译得很好(除了未经检查的警告):
def f(a: Any) = a match { case ListAlt() => 1 case Nil => 2 case _ => 0 }
scala> f(List())
res2: Int = 1
scala> f(Nil)
res3: Int = 1
scala> f(4)
res4: Int = 0
所以我想知道模式匹配器实现是否有Nil
和List()
的特殊框。可能它将List()视为文字......