为什么编译?

时间:2011-08-07 21:08:17

标签: scala

为什么要编译:

scala> def last[A](a : List[A] ) : A =     
 | a match {                           
 |   case head :: Nil => Some(head) get
 |   case _ :: tail => last(tail)      
 |   case Nil => None get              
 | }                   

last: [A](a: List[A])A

虽然这显然不是:

scala> None get

java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:262)
at .<init>(<console>:6)
at .<clinit>(<console>)
at RequestResult$.<init>(<console>:9)
at RequestResult$.<clinit>(<console>)
at RequestResult$scala_repl_result(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$17.apply(Interpreter.scala:988)
at scala.util.control.Exception$C...

1 个答案:

答案 0 :(得分:12)

后面的代码只在运行时编译并失败 - 你看到的是异常,而不是编译器错误。 None继承自Option,因此必须定义方法get

编辑: 这个问题可以改为“为什么编译器会接受对None.get的明显愚蠢的调用?”。这个问题确实无法回答,所以我会在不同的层面上再多说几句话。

  • 首先澄清:为了理解编译时错误和运行时错误之间的区别,创建一个包含要测试的代码,编译并测试它的Scala程序可能是有益的。如果只在提示符下运行代码,初学者就很难理解编译时错误和运行时错误之间的区别。 不过,精明的用户将学会识别错误的不同格式。
  • 不是get愚蠢的方法,因为它可能在运行时失败吗?的确,当你的v类型为Option[T]时,您通常应该对其进行模式匹配或使用getOrElsemap等方法:即使v为空也能正常工作的方法。但有时你有这样的价值,并且已经确定v必须是Some(q):只能在那时使用。
  • 为什么,给定None的定义,编译器不能给出编译时错误? 当您调用未在接收方对象上定义的方法时,编译器会发出错误,但get类上定义了Option,因此在其{2}和Some两个子类上定义了None {1}}。只有它的主体才包含对错误的调用。
  • 为什么Option的给定定义有意义?要让用户在需要时调用get,必须在{{1}上定义}}。否则,在能够调用它之前需要进行类型转换 - 此时,更容易进行模式匹配。
  • 为什么编译器仍然没有意识到代码在运行时会失败? 因为认识到非常重要的案件很难。例如,在上面发布的函数中,不清楚Option是否会被执行;而且,如果传入一个空列表,您提供的代码将给出运行时错误;因此它是对last的通常规范的正确实现。一般情况下,您可能拥有None.get,了解v get可能是v也可能不是None可能并不重要。还有其他工具试图找到这种错误(不确定是否还有Scala),但它们也倾向于给出错误否定或误报:它们可能会报告不可能发生的问题,或者错过相关问题。最后,您真的不希望它们的输出与Scala编译器的输出结合在一起。 在编译器中进行一些基本的简单检查可能有意义,但是你有一个工程问题:不清楚额外的代码是否值得花费。