更加惯用(monadic?)方式来表达这个Scala

jon*_*son 6 monads scala

我有几个遵循这种模式的代码块:

// Dummy function defs.
def result(i : Int, d : Double, b : Boolean) = {
    if (b) d else i
}

def fA(s : String) = {7}
def fB(s : String, i : Int) = {1.0}
def fC(s : String, i : Int, d : Double) = {true}

// Actual code.
def test(s : String) : Double = {
    try {
        val a = fA(s) 
        try {
            val b = fB(s, a)
            try {
                val c = fC(s, a, b)
                result(a, b, c)
            } catch {
                case _ => result(a, b, false)
            }

        } catch {
            case _ => result(a, 0.0, false)
        }
    } catch {
        case _ => result(0, 0.0, false)
    }
}
Run Code Online (Sandbox Code Playgroud)

其中a,b,&c依次由相应的函数计算,然后将值传递给结果函数.如果在任何阶段发生异常,则使用默认值代替剩余变量.

是否有更惯用的方式来表达此代码.它让我想起Monads,因为它是一系列链式计算,如果任何计算失败,它会立即纾困.

huy*_*hjl 5

我不确定你是否可以使用monad,因为在每一步你有两个选择(例外或结果)并且忠实于原始代码,除非你不想调用fBfC函数.

我无法优雅地删除默认值的重复,所以我离开它,因为我认为它更清楚.这是我的非monadic版本基于either.foldcontrol.Exception:

def test(s : String) = {
  import util.control.Exception._
  val args = 
    allCatch.either(fA(s)).fold(err => (0, 0.0, false), a => 
      allCatch.either(fB(s, a)).fold(err => (a, 0.0, false), b =>
        allCatch.either(fC(s, a, b)).fold(err => (a, b, false), c =>
          (a, b, c))))
  (result _).tupled(args)
}
Run Code Online (Sandbox Code Playgroud)


Hea*_*ler 5

这些类型的问题正是Try旨在解决一些更多的问题(比嵌套try/catch块).

Try表示可能导致异常或返回成功计算值的计算.它有两个子类 - SuccessFailure.

非常有趣的是,这个问题突然出现了 - 几天前,我完成了一些补充和重构scala.util.Try,对于2.10版本而且这个SO问题有助于说明组合器的一个重要用例,我们最终决定包括; transform.

(截至撰写本文时,transform目前正处于夜间,将于今日或明天发布于2.10-M5之后的Scala.有关详细信息Try和用法示例,请参阅夜间文档)

使用transform(通过嵌套),可以使用Trys 实现,如下所示:

def test(s: String): Double = {
  Try(fA(s)).transform(
    ea => Success(result(0, 0.0, false)), a => Try(fB(s, a)).transform(
      eb => Success(result(a, 0.0, false)), b => Try(fC(s, a, b)).transform(
        ec => Success(result(a, b, false)), c => Try(result(a, b, c))
      )
    )
  ).get
}
Run Code Online (Sandbox Code Playgroud)