将try转换为选项而不会丢失Scala中的错误信息

时间:2017-02-08 10:46:26

标签: scala try-catch optional scala-2.11

简介

我创造了一个可爱的单行:

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
  • 或者我应该采取什么(其他)方法?

谢谢!

2 个答案:

答案 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(因为它已在评论中提出)。