I have a map and want to:
I have several ways of doing it
val map: Map[String, Int] // defined as such for simplification
// 1 short but confusing with error handling
def getValue(key: String): Int = {
map.getOrElse(key, scala.sys.error(s"No key '$key' found"))
}
// in case you don't know scala.sys.error
package object sys {
def error(message: String): Nothing = throw new RuntimeException(message)
}
// 2 verbose
def getValue(key: String): Int = {
try {
map(key)
} catch {
case _: Throwable => scala.sys.error(s"No key '$key' found")
}
}
// 3 Try and pattern matching
import scala.util.{Failure, Success, Try}
def getValue(key: String): Int = {
Try(map(key)) match{
case Success(value) => value
case Failure(ex) => sys.error(s"No key '$key' found ${ex.getMessage}")
}
}
Others solutions are welcomed. I'm looking for conciseness without being cryptic. Using standard scala ( no need for Scalaz, Shapeless ... )
Inspirations :
The most elegant way to throw an error is your (1):
map.getOrElse(key, throw /* some Exception */)
The 2nd and 3rd options should not be used: You know which actual error can happen: the map doesn't contain the key. So wrapping it in a try, or Try, is more work than necessary. And worst, it will catch other exceptions that are not meant to be. In particular Fatal exception that should not be caught.
But, the real most elegant way to manage exceptions in scala is to actually track them with types.
A simple generic way (but sometime too generic) is to use Try. As soon as your code might fail, everything is wrapped in Try and later code is called in map and flatMap (you can use for-comprehension to make it more readable)
A more specific way is to use Either (from scala) or \/
from scalaz and explicit the type of error you are managing, such as \/(MissingData, String)
for some MissingData
class you've made. Eg:
import scalaz.\/
import scalaz.syntax.either._
sealed trait KnownException
case class MissingData(message: String) extends KnownException
// type alias used for readability
type Result[A] = \/[KnownException, A]
// retrieve a param, or a MissingData instance
def getParam(param: Map[String, Int], key: String): Result[Int] = param.get(key) match {
case None => MissingData(s"no param for key $key").left
case Some(v) => v.right
}
// example on how to combine multiple \/
// using for-comprehension
def computeAPlusB(param: Map[String, Int]): Result[Int] = for {
paramA <- getParam(param, "a")
paramB <- getParam(param, "b")
} yield paramA + paramB
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With