Reputation: 21261
Why does this compile:
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
While this obviously doesn't:
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...
Upvotes: 1
Views: 248
Reputation: 6488
The latter code does compile and fails just at runtime - what you see is an exception, not a compiler error. None
inherits from Option
and thus must define the method get
.
EDIT:
The question could be rephrased as "why does the compiler accept a clearly stupid call to None.get
?". This question is indeed nontrivial to answer, so I'll spend a few more words on it at different levels.
get
stupid, since it can fail at runtime? Indeed, when you have a value v
of type Option[T]
, you should usually pattern match on it or use methods like getOrElse
, map
, and so on: methods which work even if v
is empty. But sometimes you have such a value and are already sure that v
must be Some(q)
: get should only be used then.get
is defined on the Option
class, and therefore on its two subclasses, Some
and None
. It is only its body that contains a call to error.Option
make sense? For the user to be call to call get
when he wants to, it must be defined on Option
. Otherwise one would need a typecast before being able to call it - at that point, it'd be easier to just do pattern matching.None.get
is ever going to be executed; moreover, the code you gave will give a run-time error if you pass in an empty list; therefore it is a correct implementation of the usual specification of last. In general, you might have v get
and it might be nontrivial to understand whether v
might or might not be None
. There are other tools which try to find this kind of bugs (not sure if any for Scala yet), but they also tend to give either false negatives or false positives: they might report problems which cannot happen, or miss relevant problems. In the end, you really don't want their output to be together the output of the Scala compiler.
Having some basic easy checks within the compiler might make sense, but there you have an engineering question: it's not clear if the extra code is worth its cost.Upvotes: 12