如何在scala的简洁和正确的功能流水线版本中获得简洁和正确的错误处理版本?

时间:2015-03-26 10:28:32

标签: scala

effective scala style声明如下:

  

这既简洁又正确,但几乎每个读者都会有   难以恢复作者的原意。一个   通常用于澄清的策略是命名中间结果   和参数:

val votesByLang = votes groupBy { case (lang, _) => lang }
val sumByLang = votesByLang map { case (lang, counts) =>
  val countsOnly = counts map { case (_, count) => count }
  (lang, countsOnly.sum)
}
val orderedVotes = sumByLang.toSeq
  .sortBy { case (_, count) => count }
  .reverse
如果我在评估votesByLangsumByLang的值时遇到错误,那么我无法理解的是...我怎样才能在最后得到一个recover毕竟,如果我只有.map.map ...我最后可以有一个recover。那么这种风格也可以单一恢复吗? (而且不仅可能,而且风格很好......)

或者换句话说,在这个简洁而正确的功能流水线代码中,什么是简洁和正确的错误处理?

你看到非简洁版我能够编写代码:

object MyRealMainObj extends App {

  println(
    Try(1)
      .map(doOne)
      .map(doTwo)
      .recover { // non succinct version can catch any prior error how to achieve the same in succinct version
        case e: Throwable => println("recovering from: " + e.getMessage)
    }
  )

  def doOne(i: Int): Int = { i + 1; throw new RuntimeException("failed in one") }
  def doTwo(i: Int): Int = { i + 2; throw new RuntimeException("failed in two") }
}

单个recover会捕获.map中的任何先前错误,但简洁明了我怎样才能以简洁明了的方式实现同​​样的目标呢?

1 个答案:

答案 0 :(得分:1)

听起来你期望某种范围被.recover神奇地创造出来。不是这种情况。通过一些小的简化,Try(expr)

try {
   val result = expr
   Success(result)
catch {
   e => Failure(e)
}

tryResult.map(f)是:

tryResult match {
   case Success(x) => Try(f(x))
   case f: Failure => f
}

tryResult.recover(f)

tryResult match {
  case Failure(e) if f.isDefinedAt(e) => Try(f(e))
  case other => other
}

没有特别的魔力。在每个阶段,您都有值,而不是某些try / catch范围内的代码。将这些值放在val中不会改变事物。如果您"手动调试",您会注意到您获得了一个Success对象,然后在某个时刻,您的某个地图可能会失败并返回一个失败,以下那些将继续失败,然后当你通过最后一个恢复,它可能会回到成功。中间的设置值不会有任何区别。