Jac*_*ang 6 error-handling macros scala shapeless
我目前正在集思广益,研究在scala中进行显式错误处理的最佳方法.
理想的最终产品:
基本上,我想在steriods上检查异常.
忽略异常抛出,处理这种情况的典型方法是使用Either类型(或\/来自我正在使用的Scalaz),并将left侧面作为包含所有可能错误的ADT,如下所示:
sealed trait Error_parseAndValidate
case class ParseError(msg: String) extends Error_parseAndValidate
case class ValidateError(msg: String) extends Error_parseAndValidate
def parseAndValidate(str: String): Error_parseAndValidate \/ Int = {
// can return ParseError or ValidateError
}
Run Code Online (Sandbox Code Playgroud)
但是,如果函数调用嵌套多个级别,这将变得非常繁琐:
考虑具有以下调用堆栈的此DB示例
main- > getResultWithString- >parseAndValidate / fetchResultFromDB
// error type ADT containing expected errors in parseAndValidate
// similarly for other error_* traits
sealed trait Error_parseAndValidate
case class ParseError(msg: String) extends Error_parseAndValidate
case class ValidateError(msg: String) extends Error_parseAndValidate
def parseAndValidate(str: String): Error_parseAndValidate \/ Int = {
// can return ParseError or ValidateError
}
sealed trait Error_fetchResultFromDB
case class DBError(msg: String) extends Error_fetchResultFromDB
def fetchResultFromDB(lookupId: Int): Error_fetchResultFromDB \/ Result = {
// can return DBError
}
sealed trait Error_getResultWithString
case class ErrorFromFetchResult(Error_fetchResultFromDB) extends Error_getResultWithString
case class ErrorFromParseValidate(Error_parseAndValidate) extends Error_getReusltWithString
def getResultWithString(input: String): Error_getResultWithString \/ Result = {
}
// we need to 'extract/unwrap' our error type. this is tedious!
def main() = {
getResultWithString.leftMap {
case ErrorFromFetchResult(e) => e match {
case ParseError =>
case ValidateError =>
}
case ErrorFromParseValidate(e) => e match {
case DBERror =>
}
}
}
Run Code Online (Sandbox Code Playgroud)
虽然我们已经达到了正确性,但是这很乏味,特别是当你有超过3-4级的调用堆栈时!
我所设想的是一种可扩展的析取类型,它允许我们"扩展"我们的错误.感觉有些东西shapelss可以帮助我实现这一目标,但过去没有使用它,我认为HList这种情况不适合.
我在|这里使用符号来表示两种类型之间的分离(联合).这里最重要的是我们不再需要使用ADT来包装和解包我们的错误 - 我们最终匹配的错误只是一个**平面分离*.
// you can see here we are simply 'joining' the errors from both method calls,
// instead of wrapping it in a ADT which requires unwrapping
def getResultWithString(input: String): Error_parseAndValidate|Error_fetchResultFromDB \/ Result = {
for {
lookupId <- parseAndValidate(input)
result <- fetchResultFromDB(lookupId)
} yield result
}
type Error_parseAndValidate = ParseError | ValidateError
def parseAndValidate(input: String): Error_parseAndValidate \/ Int = {
//returns ParseError or ValidationError
}
type Error_fetchResultFromDB = DBError
def fetchResultFromDB(lookupId: Int): Error_fetchResultFromDB \/ Result = {
//returns DBError
}
def main() = {
getResultWithString("bad string").leftMap {
case ParseError => //...
case ValidateError => //...
case DBError => //...
}
}
Run Code Online (Sandbox Code Playgroud)
这个 SO问题的答案有一些解决方案,但解决方案不可扩展.
是否有可能实现我在scala中的环境?
| 归档时间: |
|
| 查看次数: |
444 次 |
| 最近记录: |