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

Ray*_*non 4 dictionary scala scala-option

我有一张地图,想要:

  • 检索值而不处理Option
  • 没有这样的密钥时记录消息.
  • 很好,当密钥不存在时返回默认值(除了记录消息).这是可选的,因为当代码在这里失败时,它不应该继续.

我有几种方法可以做到这一点

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)
}
Run Code Online (Sandbox Code Playgroud)
// 2 verbose
def getValue(key: String): Int = {
    try {
      map(key)
    } catch {
      case _: Throwable => scala.sys.error(s"No key '$key' found")
    }
}
Run Code Online (Sandbox Code Playgroud)
// 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}")
    }
}
Run Code Online (Sandbox Code Playgroud)

欢迎其他解决方案. 我正在寻找简洁而不神秘.使用标准scala(不需要Scalaz,Shapeless ......)

灵感:

Juh*_*uh_ 6

抛出错误最优雅的方法是你的(1):

map.getOrElse(key, throw /* some Exception */)

不应使用第2和第3个选项:您知道可能发生的实际错误:映射不包含密钥.因此,尝试将其包装起来,或尝试,是比必要的更多的工作.最糟糕的是,它会捕获其他不合适的例外情况.特别是不应该被捕获的致命异常.


现在,在scala中管理异常的真正最优雅的方法是使用类型实际跟踪它们.

一个简单的通用方法(但有时太通用)是使用Try.一旦你的代码可能失败,所有内容都包含在Try中,稍后在map和flatMap中调用代码(你可以使用for -reherehension使其更具可读性)

更具体的方法是使用Either(来自scala)或\/来自scalaz并显式显示您正在管理的错误类型,例如您已经创建的\/(MissingData, String) 某个MissingData类.例如:

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
Run Code Online (Sandbox Code Playgroud)