使代码更具功能可读性

时间:2014-04-01 17:49:07

标签: scala

我正在看下面的代码片段。当map-getOrElse和嵌套模式匹配在代码中增加时,它看起来不那么优雅。你有什么更好的选择吗?

 case MyMessage =>
        val image = (request \ "image").asOpt[String]
        image.map { im =>
          val conf = (request \ "confirmation").asOpt[String]
          conf.map { cf =>
            //code to retrieve ride

            ride match {
              case Some(r) =>
                if (booleanCondition) sender ! SuccessCommand(JsBoolean(true), command)
                else sender ! FailureCommand("Problem updating", command)
              case None => sender ! FailureCommand("Ride empty", command)
            }

          } getOrElse (sender ! FailureCommand("Missing number", command))

        } getOrElse (sender ! FailureCommand("Missing image", command))

3 个答案:

答案 0 :(得分:2)

每当您使用生成Option的函数映射Option时,您应该考虑是否应该使用flatMap

def f(x: Int): Option[Int] = Some(x + 1)

f(1).flatMap(x => f(x)).flatMap(y => f(y))               // Some(4)
f(1).flatMap(x => f(x)).flatMap(y => f(y)).getOrElse(0)  // 4

你也可以使用for-comprehensions,当你有很长的链代码时,这对于使用干净的代码非常好:

(for(x <- f(1); y <- f(x); z <- f(y)) yield z).getOrElse(0)

答案 1 :(得分:2)

解决此问题的另一种方法是从各种辅助函数返回Either[Command,String],而不是Option。这将允许您使用for理解,如下所示:

val result = for {
  i <- getImage().right
  c <- getConf().right
  r <- getRide().right
  z <- check(r).right
} yield z

// extract either left or right, whichever is occupied
sender ! result.fold(identity, _ => success()) 

这具有所需的属性,我们会在遇到错误时立即停止,并捕获该特定错误 - 或者继续成功结束。

答案 2 :(得分:1)

我认为您应该能够将大部分内容合并到Option.fold()中,大致如下:

case MyMessage =>
  sender ! 
    getImage().fold(fail("Missing image")) { im =>
      getConf().fold(fail("Missing number")) { conf => // conf isn't used
        getRide().fold(fail("Ride empty")) { r =>
          if (booleanCondition) succeed(true)
          else fail("Problem updating")
        }
      }
    }

在这种情况下,这比flatMaporElse简洁得多(见下文)

Option.fold(ifEmpty){f}如果选项为空则返回ifEmpty(懒惰地评估),或者如果选项已满,则返回函数f

上面的代码假定您创建了帮助函数以获取各种选项(或者您可以内联相关代码)。它还假设您将命令的创建提取到一个或两个辅助函数中,以避免对command的所有重复引用。

为了进行比较,使用flatMap的解决方案类似于:

case MyMessage =>
  sender ! 
    getImage().flatMap { im =>
      getConf().flatMap { conf =>
        getRide().flatMap { r =>
          if (booleanCondition) Some(succeed(true))
          else Some(fail("Problem updating"))
        }.orElse(Some(fail("Ride Empty")))
      }.orElse(Some(fail("Missing number")))
    }.getOrElse(fail("Missing image"))

您可以通过让辅助方法(failsucceed)的变体返回Some[Command]而不是Command

来略微缩短