Scala - Pattern Matching with Caution!- Is it a bug?

772 views
Skip to first unread message

Tarun Kumar

unread,
Feb 22, 2016, 12:51:41 PM2/22/16
to scala-user
Hi,

I am struggling to understand why the two snippets below will behave differently or 'null' is the problem here?. One executes successfully and other throws the runtime scala.MatchError.

Snippet 1: 

 val str = "HELP"

val perfectTuple: (String, String) = str match {
 
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
 
  }


==================

Snippet 2:

val str = "HELP"

val (firstPart:String, secondPart:String) = str match {
 
  case "NO-HELP" => ("First Help", "Second Help")
  case "OTHER-HELP" => ("I won't Help!", "Even,I won't Help!")
  case "HELP" => (null,"Single Help")
  case _ => throw new NoSuchMethodException
 
  }

======================

As we can see that there is a little difference between the two snippets , one storing the result in a tuple 2 value and the other is extracting the value out of tuple 2 and storing them in two values. Snippet1 executes successfully but snippet 2 throws the runtime error , below:

Exception in thread "main" scala.MatchError: (null,Single Help) (of class scala.Tuple2)
at com.trn.scala.problems.P05$.delayedEndpoint$com$trn$scala$problems$P05$1(P05.scala:10)
at com.trn.scala.problems.P05$delayedInit$body.apply(P05.scala:6)
at scala.Function0$class.apply$mcV$sp(Function0.scala:34)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:76)
at scala.App$$anonfun$main$1.apply(App.scala:76)
at scala.collection.immutable.List.foreach(List.scala:381)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
at scala.App$class.main(App.scala:76)
at com.trn.scala.problems.P05$.main(P05.scala:6)
at com.trn.scala.problems.P05.main(P05.scala)

I have executed this on scala 2.10.5 as well as 2.11.7 .P05 is my object name above. 

Please advise if i am missing the obvious here?

Thanks
Tarun

Oliver Ruebenacker

unread,
Feb 22, 2016, 1:04:21 PM2/22/16
to Tarun Kumar, scala-user

     Hello,

  Can be boiled down to:

scala> val (x: String, y: String) = (null, "Yo"): (String, String)
scala.MatchError: (null,Yo) (of class scala.Tuple2)
  ... 33 elided

  The notation val (..., ...) = ... is trying to match Tuple2, and null is not a String.

     Best, Oliver

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Oliver Ruebenacker
Senior Software Engineer, Diabetes Portal, Broad Institute

Seth Tisue

unread,
Feb 22, 2016, 1:09:36 PM2/22/16
to scala-user
at it's heart, the issue is that

scala> null.isInstanceOf[String]
res0: Boolean = false

yet this is neither a compile-time nor a run-time error:

scala> val x: String = null
x: String = null

so null both is and isn't a String, depending on how you pose the question. this is standard on the JVM; Java behaves the same way.

in Scala, we try to avoid ever using null at all, unless we're interoperating with legacy Java code. so in normal, idiomatic Scala code, the issue never arises.

Tarun Kumar

unread,
Feb 22, 2016, 1:21:03 PM2/22/16
to Seth Tisue, cur...@gmail.com, scala-user
Thanks Oliver, Seth

so this would lead me to compliment my earlier question with the following :

if i need to return Multiple values (ie. Tuple N) from pattern matching and that too may be containing java objects (which can return null), Would it be advisable to receive them in the Tuple N definition ( like in perfectTuple Example above) and then use the values like :

perfectTuple._1, perfectTuple._2 and so on ...

instead of assigning them into different variables like in the snippet posted which is failing?

because referring to your example, i can use like the below to extract the value

val x: (String, String) = (null, "Yo"): (String, String)

x._1
x._2

...

So the assumption of using 'val x: (String, String)' always will hold true, as it will always protect from null's?

then where the other kind of 'val (x: String, y: String)' would be useful ?

Thanks
Tarun

Adriaan Moors

unread,
Feb 22, 2016, 1:37:15 PM2/22/16
to Tarun Kumar, Seth Tisue, Oliver Ruebenacker, scala-user
The null check stems from the type test in the pattern on the RHS of your assignment. In patterns, (p: String) implies p's type is String, as well as that p is not null.

It's possible to return null in a tuple and immediately match on it to get them out -- here are some variations on this theme:


scala> val (null1, null2) = (null: String, null: Any)
null1: String = null
null2: Any = null


scala> val (1::rest, null2) = (List(1,2,3), null: Any)
rest: List[Int] = List(2, 3)
null2: Any = null

For extra credit/fun:
scala> val (foo@(_: String | null), bar) = (null: String, null: Any)
foo: String = null
bar: Any = null



More generally, a `val (x1@p1, ... xN@pN) = e` desugars to something like

val res = e match { case (x1@p1, ... xN@pN) =>  (x1,... xN) } 
val x1 = res._1

val xN = res._N

som-snytt

unread,
Feb 24, 2016, 1:06:38 AM2/24/16
to scala-user, tarun...@gmail.com, se...@tisue.net, cur...@gmail.com

So it really is true that the pattern matcher guy has more fun.

Alan Burlison

unread,
Feb 24, 2016, 8:32:23 AM2/24/16
to Adriaan Moors, Tarun Kumar, Seth Tisue, Oliver Ruebenacker, scala-user
On 22/02/2016 18:36, Adriaan Moors wrote:

> The null check stems from the type test in the pattern on the RHS of your
> assignment. In patterns, (p: String) implies p's type is String, as well as
> that p is not null.

Isn't a better option to wrap the possibly null string in an Option, and
match on that?

scala> def giveMeNull(hitMe: Boolean) = if (hitMe) null else "hello"
giveMeNull: (hitMe: Boolean)String

scala> Option(giveMeNull(false)) match { case None => "was null"; case
Some(s) => s }
res3: String = hello

scala> Option(giveMeNull(true)) match { case None => "was null"; case
Some(s) => s }
res4: String = was null

That's my default way of dealing with possibly-null-returning Java APIs,
because Option helpfully maps Option(null) onto None.

scala> Option(null)
res5: Option[Null] = None

--
Alan Burlison
--

som-snytt

unread,
Feb 24, 2016, 2:59:59 PM2/24/16
to scala-user, adr...@typesafe.com, tarun...@gmail.com, se...@tisue.net, cur...@gmail.com

There was just a StackOverflow question that turned on what null.asInstanceOf[A] means when A is inferred Nothing. (That was in a default arg expression.)

The answer had some difficulty reasoning about that expr, but less difficulty if the expr were Option[A] instead of A. (Because None vs Nothing)
Reply all
Reply to author
Forward
0 new messages