将尝试转换为选项而不丢失 Scala 中的错误信息
Convert try to option without losing error information in Scala
简介
我创造了一个可爱的单线:
Option("something").map(_ => Try("something else")).flatten.getOrElse("default")
实际上没有编译,错误:
Error:(15, 31) Cannot prove that scala.util.Try[String] <:< Option[B].
Option("").map(_ => Try("")).flatten.getOrElse("");}
^
所以我找到了解决方法:
Option("something").flatMap(_ => Try("something else").toOption).getOrElse("default")
但是,问题
我的同事警告我,我的构造实际上正在丢失错误信息。这是事实,但在实际应用中 - 是不可接受的。
摆脱所有重复后我得到了:
implicit class CoolTry[T](t: Try[T]) extends StrictLogging {
def toOptionSE: Option[T] = t match {
case Success(s) => Some(s)
case Failure(ex) =>
logger.error(ex.getMessage, ex)
None
}
}
使用:
Option("something").flatMap(_ => Try(new Exception("error")).toOptionSE).getOrElse("default")
问题
我相信每个应用程序中都有很多类似的情况,我只是不知道是我的方法不好还是 Try().toOption
只是做错了?
我知道日志记录是一个副作用,但是在使用 Try
时我想每个人 是否 预料到如果出现问题?
-
implicit
class 可以改进吗?
- 是否有任何实用程序库以我的方式处理
Try().toOption
?
- 或者我应该采取什么(其他)方法?
谢谢!
每次将 Try[T]
更改为 Option[T]
时强制记录是 IMO 的不良影响。当您进行这样的转换时,您明确承认您并不真正关心故障的内部结构,如果它发生了。您想要的只是访问结果(如果存在)。大多数时候你说 "well, this is undesirable, I always want to log exceptions",但有时你只关心最终结果,所以处理 Option[T]
就足够了。
Or what (other) approach should I take here?
在这里使用 Either[A, B]
是一个选项,特别是在 Scala 2.12 中,当它变得右偏时。您可以简单地映射它并仅在最后检查是否有错误然后记录(使用 Scala 2.12.1 创建):
val res: Either[Throwable, String] = Option("something")
.map(_ => Try("something else").toEither)
.getOrElse(Right("default"))
.map(str => s"I got my awesome $str")
理想情况下 (IMO) 将日志记录的副作用推迟到最后可能的时间点会更好。
一个可能的改进是让用户crystal清楚正在发生的事情;在 implicit
被编译器注入某处和明显的 "pure" 名称 (toOptionSE
) 之间,对于第二个阅读 and/or 修改的开发人员来说,可能不清楚发生了什么你的代码。此外,您正在修复处理错误情况的方式,不要留下以不同于记录错误的方式处理它的机会。
您可以利用投影来处理错误,例如在 Try
上定义的 failed
投影。如果你真的想在一行中流畅地完成这项工作,你可以像这样利用 implicit class
es。
implicit class TryErrorHandlingForwarding[A](t: Try[A]) {
def onError(handler: Throwable => Unit): Try[A] = {
t.failed.foreach(handler)
t
}
}
// maybe here you want to have an actual logger
def printStackTrace: Throwable => Unit =
_.printStackTrace
Option("something").
flatMap(_ => Try(???).onError(printStackTrace).toOption).
getOrElse("default")
此外,我假设无论出于何种原因,您不能从一开始就使用 Try
(正如评论中所建议的那样)。
简介
我创造了一个可爱的单线:
Option("something").map(_ => Try("something else")).flatten.getOrElse("default")
实际上没有编译,错误:
Error:(15, 31) Cannot prove that scala.util.Try[String] <:< Option[B].
Option("").map(_ => Try("")).flatten.getOrElse("");}
^
所以我找到了解决方法:
Option("something").flatMap(_ => Try("something else").toOption).getOrElse("default")
但是,问题
我的同事警告我,我的构造实际上正在丢失错误信息。这是事实,但在实际应用中 - 是不可接受的。
摆脱所有重复后我得到了:
implicit class CoolTry[T](t: Try[T]) extends StrictLogging {
def toOptionSE: Option[T] = t match {
case Success(s) => Some(s)
case Failure(ex) =>
logger.error(ex.getMessage, ex)
None
}
}
使用:
Option("something").flatMap(_ => Try(new Exception("error")).toOptionSE).getOrElse("default")
问题
我相信每个应用程序中都有很多类似的情况,我只是不知道是我的方法不好还是 Try().toOption
只是做错了?
我知道日志记录是一个副作用,但是在使用 Try
时我想每个人 是否 预料到如果出现问题?
-
implicit
class 可以改进吗? - 是否有任何实用程序库以我的方式处理
Try().toOption
? - 或者我应该采取什么(其他)方法?
谢谢!
每次将 Try[T]
更改为 Option[T]
时强制记录是 IMO 的不良影响。当您进行这样的转换时,您明确承认您并不真正关心故障的内部结构,如果它发生了。您想要的只是访问结果(如果存在)。大多数时候你说 "well, this is undesirable, I always want to log exceptions",但有时你只关心最终结果,所以处理 Option[T]
就足够了。
Or what (other) approach should I take here?
在这里使用 Either[A, B]
是一个选项,特别是在 Scala 2.12 中,当它变得右偏时。您可以简单地映射它并仅在最后检查是否有错误然后记录(使用 Scala 2.12.1 创建):
val res: Either[Throwable, String] = Option("something")
.map(_ => Try("something else").toEither)
.getOrElse(Right("default"))
.map(str => s"I got my awesome $str")
理想情况下 (IMO) 将日志记录的副作用推迟到最后可能的时间点会更好。
一个可能的改进是让用户crystal清楚正在发生的事情;在 implicit
被编译器注入某处和明显的 "pure" 名称 (toOptionSE
) 之间,对于第二个阅读 and/or 修改的开发人员来说,可能不清楚发生了什么你的代码。此外,您正在修复处理错误情况的方式,不要留下以不同于记录错误的方式处理它的机会。
您可以利用投影来处理错误,例如在 Try
上定义的 failed
投影。如果你真的想在一行中流畅地完成这项工作,你可以像这样利用 implicit class
es。
implicit class TryErrorHandlingForwarding[A](t: Try[A]) {
def onError(handler: Throwable => Unit): Try[A] = {
t.failed.foreach(handler)
t
}
}
// maybe here you want to have an actual logger
def printStackTrace: Throwable => Unit =
_.printStackTrace
Option("something").
flatMap(_ => Try(???).onError(printStackTrace).toOption).
getOrElse("default")
此外,我假设无论出于何种原因,您不能从一开始就使用 Try
(正如评论中所建议的那样)。