Scala 处理选项和从 Scala Map 抛出异常的最优雅方式

Scala most elegant way to handle option and throw exception from Scala Map

我有一张地图,想要:

我有几种方法可以做到

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