我创造了一个可爱的单行:
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
课程可以改进吗? Try().toOption
? 谢谢!
答案 0 :(得分:3)
每次将Try[T]
更改为Option[T]
时强制记录都会产生不良影响IMO。当你进行这样的转换时,你明确承认你并不真正关心失败的内部,如果它发生的话。您只需要访问结果(如果存在)。大多数时候你说“好吧,这是不可取的,我总是想记录异常”,但有时你只关心最终结果,所以处理Option[T]
就足够了。
或者我应该采取什么(其他)方法?
在这里使用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)将记录副作用推迟到最后一点可能会更好。
答案 1 :(得分:3)
一个可能的改进是让用户清楚地知道发生了什么;在编译器注入的implicit
和明显的“纯”名称(toOptionSE
)之间,对于第二个开发人员阅读和/或修改代码的内容可能并不明显。此外,您正在修复如何处理错误情况,并且不会有机会以不同的方式处理错误情况。
您可以通过利用投影来处理错误,例如failed
上定义的Try
投影。如果你真的想要在一行中流利地做到这一点,你可以像这样利用implicit class
。
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
(因为它已在评论中提出)。