Pattern guards don't handle nulls well, and, also, introducing a pattern guard appears to make a default _ not match on null values:
scala> def guard_fails(s: String) {
| s match {
| case s: String if s == null => println("null")
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails: (String)Unit
scala> guard_fails("test")
s=test
scala> guard_fails(null)
scala.MatchError
at .guard_fails(
at .
at .
at RequestResult$.
at RequestResult$.
at RequestResult$result(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMeth...
scala>
scala> def guard_fails2(s: String) {
| s match {
| case s: String if s != null => println("s=" + s)
| case _ => println("default")
| }
| }
guard_fails2: (String)Unit
scala> guard_fails2("test")
s=test
scala> guard_fails2(null)
scala.MatchError
at .guard_fails2(
at .
at .
at RequestResult$.
at RequestResult$.
at RequestResult$result(
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMet...
scala>
To code this match defensively, you have to define a null pattern explicitly:
scala> def ok(s: String) {
| s match {
| case s: String if true => println("s=" + s)
| case null => println("null")
| case _ => println("default")
| }
| }
ok: (String)Unit
scala> ok("test")
s=test
scala> ok(null)
null
scala>
4 comments:
Sounds like a bug to me. I would file it in Scala's Trac: https://lampsvn.epfl.ch/trac/scala
Ahh, that's a tricky little bugger. I think this behaves according-to-spec. (I omit judgement as to whether the spec's decision is good or bad.)
Section 8.2 of SLS on Type Patterns says: "A reference to a class C [...] matches any non-null instance of the given class."
So it's the String annotation here that's excluding the null from the match. In particular, the following code works as one would expect:
def guard(s: String) = s match {
__case p if p == null => "null"
__case p if p != null => p
}
I half agree with Jorge. If you match with `case x: String =>`, null is excluded. That's just like null is always excluded in isInstanceOf tests (in both Java and Scala). So the program should have taken the third, default case. However, it seems that the inaccessible guard
`if x == null` fooled the pattern matcher. So it would be good if you could file a ticket on that.
Consider using Option instead of nulls.
Post a Comment