Scala 处理选项和从 Scala Map 抛出异常的最优雅方式
Scala most elegant way to handle option and throw exception from Scala Map
我有一张地图,想要:
- 检索值而不处理选项
- 当没有这样的密钥时记录一条消息。
- 很高兴 return 键不存在时的默认值(除了记录消息)。这是可选的,因为当代码在这里失败时,它不应该继续。
我有几种方法可以做到
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}")
}
}
欢迎其他解决方案。
我正在寻找简洁而不是神秘的。使用标准 scala(不需要 Scalaz、Shapeless ...)
灵感:
最优雅的抛出错误的方法是你的 (1):
map.getOrElse(key, throw /* some Exception */)
不应使用第二个和第三个选项:您知道可能会发生哪个实际错误:地图不包含密钥。因此,将其包装在一个 try 或 Try 中是不必要的工作。
最糟糕的是,它会捕捉到其他本不该出现的异常。特别是不应捕获的致命异常。
但是,在 scala 中管理异常的真正最优雅的方法 是使用类型实际跟踪它们。
一种简单的通用方法(但有时过于通用)是使用 Try。一旦您的代码可能会失败,所有内容都将包含在 Try 中,稍后的代码将在 map 和 flatMap 中调用(您可以使用 for-comprehension 使其更具可读性)
一种更具体的方法是使用 Either(来自 scala)或来自 scalaz 的 \/
并明确您正在管理的错误类型,例如 \/(MissingData, String)
对于某些 MissingData
class 你做到了。例如:
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
我有一张地图,想要:
- 检索值而不处理选项
- 当没有这样的密钥时记录一条消息。
- 很高兴 return 键不存在时的默认值(除了记录消息)。这是可选的,因为当代码在这里失败时,它不应该继续。
我有几种方法可以做到
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}")
}
}
欢迎其他解决方案。 我正在寻找简洁而不是神秘的。使用标准 scala(不需要 Scalaz、Shapeless ...)
灵感:
最优雅的抛出错误的方法是你的 (1):
map.getOrElse(key, throw /* some Exception */)
不应使用第二个和第三个选项:您知道可能会发生哪个实际错误:地图不包含密钥。因此,将其包装在一个 try 或 Try 中是不必要的工作。 最糟糕的是,它会捕捉到其他本不该出现的异常。特别是不应捕获的致命异常。
但是,在 scala 中管理异常的真正最优雅的方法 是使用类型实际跟踪它们。
一种简单的通用方法(但有时过于通用)是使用 Try。一旦您的代码可能会失败,所有内容都将包含在 Try 中,稍后的代码将在 map 和 flatMap 中调用(您可以使用 for-comprehension 使其更具可读性)
一种更具体的方法是使用 Either(来自 scala)或来自 scalaz 的 \/
并明确您正在管理的错误类型,例如 \/(MissingData, String)
对于某些 MissingData
class 你做到了。例如:
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