Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala: How to suppress unchecked warnings / do pattern matching of maps correctly?

Tags:

scala

In a test call a third-party function which returns an Option[Any]. I know if this function returns a Map, it is a Map[String, Any]. In this case, I want to check for individual map elements.

theFunction(...) match {
  case Some(m: Map[String, Any]) =>
    m("some key") match {
      case some_condition => ... (my check)
    }
  case _ => fail("Invalid type")
}

But the compiler warns that case Some(m: Map[String, Any]) is unchecked. When I use Map[_,_] instead, the compiler bails out at the point where I check m("some key").

How do I suppress this warning, or better: how do I do this check correctly? The only approach I can think of is something like

theFunction(...) match {
  case Some(m: Map[_,_]) =>
    val m1: Map[String, Any] = m.toSeq.map(t => t._1.asInstanceOf[String] -> t._2).toMap
    m1("some key") match {
      case some_condition => ... (my check)
    }
}

but in my eyes, this looks ugly and introduces unnecessary converting of the map to a Seq and vice-versa.

like image 552
rabejens Avatar asked Apr 30 '15 13:04

rabejens


2 Answers

You can use the unchecked annotation to suppress compiler warnings like these (for non-exhaustive matches and fruitless matches due to type erasure).

From the scaladoc:

An annotation to designate that the annotated entity should not be considered for additional compiler checks. Specific applications include annotating the subject of a match expression to suppress exhaustiveness warnings, and annotating a type argument in a match case to suppress unchecked warnings.

Such suppression should be used with caution, without which one may encounter scala.MatchError or java.lang.ClassCastException at runtime. In most cases one can and should address the warning instead of suppressing it.

Example:

val m: Any = Some(Map("a" -> 1, "b" -> "b"))
m match {
    case Some(m: Map[String, Any]) => println("map")
    case None => println("None")
}

<console>:30: warning: non-variable type argument String in type pattern scala.collection.immutable.Map[String,Any] (the underlying of Map[String,Any]) is unchecked since it is eliminated by erasure
                  case Some(m: Map[String, Any]) => println("map")

We can add the @unchecked annotation to Map[String, Any] within the match:

m match {
    case Some(m: Map[String, Any] @unchecked) => println("I'm a map!")
    case None => println("None")
}

// Exiting paste mode, now interpreting.

I'm a map!

No more warning. But this is obviously no more safe than allowing the warning, and could lead to some surprising match errors or ClassCastExceptions.

like image 178
Michael Zajac Avatar answered Oct 06 '22 15:10

Michael Zajac


Well, I am not sure, if it is related to question, but you can wherever you want add type annotations.

For example, I have

type H = List[Int]
def sum_dummy(h: H): Int = {
   if (isEmpty(h)) throw new NoSuchElementException()
   else (h:H @unchecked) match {
      case x::list => x + func(list)
   }
}

you can rewrite example with case Nilusage for full idiomatic scala, but I wrote it for type annotation example.

like image 23
gaussblurinc Avatar answered Oct 06 '22 13:10

gaussblurinc