Nil和List作为Scala中的case表达式

时间:2011-09-25 22:40:47

标签: scala

此代码编译:

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()=> ... 没有标记相同的错误?

4 个答案:

答案 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

所以我想知道模式匹配器实现是否有NilList()的特殊框。可能它将List()视为文字......