【问题标题】:Scala DRYing try/catchScala DRYing try/catch
【发布时间】:2012-04-24 00:35:39
【问题描述】:

我发现自己在重复简单的 try/catch 块,IMO 应该是 1-liners。

例如,假设我需要将 yyyymmdd 格式的 uri 字符串日期转换为 Joda-Time。使用标准的 try/catch 块,转换方法如下所示:

def ymd2Date(ymd: Option[String]) = ymd match {
  case Some(date) =>
    try { Right( ymdFormat.parseDateTime(date) ) }
    catch { case e => 
      log.info(e.getMessage)
      Left(None) 
    }
  case None => 
    Left(None) // no uri date param
}
val ymdFormat = DateTimeFormat.forPattern("yyyyMMdd")

工作得很好,意图很明确,但是当我对 all 非关键事件进行这种尝试/捕获日志记录时,我会寻找一种方法来将其干燥。搜索使我在 scala.util.control.Exception 上找到了这个SO post。有帮助,但仍然有点难以摸索/让它以我想要的方式工作。用简单的英语我只想说,“catch some-action get-result log-error-type”。

所以,我根据 control.Exception 我感兴趣(或理解有用)的部分破解了这个:

class Catcher[T](f: => T) {   
  type Logger = (=> Any) => Unit

  def either[T]( logger: => Logger ) = {
    try { Right(f) }
    catch { case e => 
      logger(e.getMessage)
      Left(None)
    }
  }
}
def catching[T](f: => T) = new Catcher(f)

然后像这样代替 try/catch 使用:

catching( ymdFormat.parseDateTime(date) ) either log.info

可以添加选项、msg 前缀等,但是……最好找到一种方法来让 control.Exception 像上面那样工作,因为 TypeSafe 团队将更好地生成代码世界超出我的想象。

有谁知道如何使用 control.Exception 创建这种语法,其中可以按名称传入记录器函数以在 catch 块中使用?

如果有 control.Exception 的“用例”会很棒,但我觉得这个实用程序适用于更高级的 Scala 开发人员

【问题讨论】:

  • 你为什么使用Either?如果你要输入LeftNone,那么你似乎可以使用Option[T]
  • 好点,我想把结果折叠起来,而且 Either 看起来很自然...
  • 也就是说目前没有选项折叠,或者至少没有内置在语言中

标签: scala logging try-catch dry


【解决方案1】:

一个简单的例子:

import scala.util.control.Exception._

def throwingStuff {
  throw new Exception("Hello World!")
}

catching(classOf[Exception]).withApply{err => println(err.toString); None}.apply(Some(throwingStuff))

您可以使用withApply 覆盖Catch 类的应用程序逻辑来执行诸如写入日志之类的操作。

【讨论】:

  • 返回类型不太适用于此。 withApplyThrowable 转换为返回值,所以你永远不会得到Left,而Right 的类型将是AnyAnyRef
  • 是的,在您发表评论之前就实现并编辑了!不过,使用 Some 是个好主意。
  • 需要保留类型。如果我选择 Option[T],该类型会被保留/可访问吗?
  • 是的 - apply(Some(throwingStuff)) 将像丹尼尔的回答一样工作。
  • hmmm,类型需要在两种情况下保留,无论是,还是选项。在我给出的示例中,我使用了 Either,因为我想将结果与选项匹配 Some/None 或 getOrElse 折叠起来。无论如何,两者都需要在类型保存方面加以考虑。我会看看什么类型回来......
【解决方案2】:

这应该做你想做的:

import scala.util.control.Exception
def log(logger: => Logger)(e: Throwable) = {
    logger(e.getMessage)
    None
}
Exception.allCatch withApply log(logger) apply Some(ymdFormat.parseDateTime(date))

但在我看来,Scalaz Validation 会更好地处理这类事情。

【讨论】:

  • 是的,还没有冒险下 Scalaz 行,只是在 Scala 本身中弄湿了我的脚。最基本形式的验证看起来像 Either,但随后曲线迅速上升。这是待办事项...
  • 这很酷,顺便说一句,看起来我可以放弃我的黑客并采用我以外的人会理解的方法;-)
  • @virtualeyes - 对于它的价值,你的“hack”对我来说比 Daniel 的方法更清晰(如果你不叫它 catching 而是 tryOrLog 或其他东西,它会更清楚)这并不意味着第一个参数本身会被捕获)。
  • @Rex,“捕捉”只是遵循 control.Exception 约定。我以前也是投手,所以我想我喜欢接球手,或者至少喜欢接球;-) tryOrLog 在语义上确实更清晰...
猜你喜欢
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 2013-04-20
  • 2020-03-13
相关资源
最近更新 更多