Tom Medley Tom Medley - 3 months ago 13
Scala Question

Scala case statement failing

I'm trying the following:

def getStatus: MyStatus = {
(mPending, mPublished) match {
case (None, None) => MyStatus.inactive
case (pending: Option[Edit], None) => MyStatus.neverPublished
case (None, published: Option[Edit]) => if (published.get.isSuspended) MyStatus.suspended else MyStatus.published
case (pending: Option[Edit], published: Option[Edit]) =>
if (published.get.isSuspended)
MyStatus.suspendedWithChanges
else
MyStatus.publishedWithChanges
}
}


As far as I can see, if it gets to the last
case
, neither
Option
should be
None
, but I get the following:

play.api.UnexpectedException: Unexpected exception[ClassCastException: null]
....
Caused by: java.lang.ClassCastException: scala.None$ cannot be cast to com.fredley.Edit


thrown on
if (published.get.isSuspended)
. What's going on?

Answer

Option, Some and None

You can see what is going on with a simpler example:

val foo: Option[String] = None

foo match {
  case x: Option[String] => "Yep! It's an option!"
  case None => "Foo is None!"
}  // res0: String = Yep! It's an option!

Because None is actually a subclass of Option, a None value will match Option[String]. The correct solution is to use Some[String]:

foo match {
  case x: Some[String] => "It's a string!"
  case None => "Foo is None!"
}  // res0: String = Foo is None!

Documentation shows the implemention of Option.

Note on Conditionals

While you can embed your conditional statements as you are now, case matching allows you to include them in the case itself:

val suspended = true
foo match {
  case x: Some[String] => "It's a string!"
  case None if suspended => "None but suspended!"
  case None => "Foo is None!"
}  // res0: String = None but suspended!

So you can convert your code to:

def getStatus: MyStatus = {
  (mPending, mPublished) match {
    case (None, None) => MyStatus.inactive
    case (pending: Some[Edit], None) => MyStatus.neverPublished
    case (None, published: Some[Edit]) if published.get.isSuspended => MyStatus.suspended 
    case (None, published: Some[Edit]) => MyStatus.published
    case (pending: Option[Edit], published: Option[Edit]) if published.get.isSuspended => MyStatus.suspendedWithChanges
    case (pending: Option[Edit], published: Option[Edit]) => MyStatus.publishedWithChanges
  }
}

This may or may not be easier for you to parse, but is an option if you're finding the conditional branching to be becoming opaque.