假设我想在Scala中编写以下逻辑

val xdir = System.getProperty(“XDir”)
如果(xdir == null)
error(“No XDir”)//记录错误并退出

val ydir = System.getProperty(“YDir”)
如果(ydir == null)
错误(“没有YDir”)

如果(!new File(xdir).isDirectory)
错误(“XDir不是目录”)

如果(!new File(ydir).isDirectory)
错误(“YDir不是目录”)

如果(!new File(xdir).exists)
错误(“XDir不存在”)

如果(!new File(ydir).exists)
错误(“YDir不存在”)
...
(等等)

在Scala中编写此验证链的最佳方法是什么?

最佳答案

这里有一些有用的东西:

def sysValue(prop: String) = Option(System.getProperty(prop)) //returns Option[String]

def trySysValue(prop: String) = //returns Either[String, String]
  sysValue(prop) map Right getOrElse Left("Absent property: " + prop)

然后,您可以通过右投影使用Either的单子(monad)组合
val batch = //batch is Either[String, (File, File)]
  for {
    x  <- trySysValue("XDir")).right
    xf <- dir(x).right
    y  <- trySysValue("YDir").right
    yf <- dir(y).right
  }
  yield (xf, yf)

在哪里:
def dir(s: String) = { //returns Either[String, File]
  val f = new File(s)
  if (!f.exists()) Left("Does not exist: " + f)
  else if (!f.isDir()) Left("Is not a directory: " + f)
  else Right(f)
}
Either的左侧将是一条错误消息。这种单子(monad)组成很快就失败了。您可以使用 scalaz XDir实现构图,该构图将累积所有失败(例如,如果YDirValidation都不存在,则您将看到两条消息)。在这种情况下,代码如下所示:
def trySysValue(prop: String) = //returns Validation[String, String]
  sysValue(prop) map Success getOrElse ("Absent property: " + prop).fail

def dir(s: String) = {
  val f = new File(s)
  if (!f.exists())("Does not exist: " + f).fail
  else if (!f.isDir()) ("Is not a directory: " + f).fail
  else f.success
}

val batch = //batch is ValidationNEL[String, (File, File)]
  (trySysValue("XDir")) flatMap dir).liftFailNel <|*|> (trySysValue("YDir")) flatMap dir).liftFailNel

09-25 16:09