6

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

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

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

if (!new File(xdir).isDirectory)
   error("XDir 不是目录")

if (!new File(ydir).isDirectory)
   error("YDir 不是目录")

if (!new File(xdir).exists)
   error("XDir 不存在")

if (!new File(ydir).exists)
   error("YDir 不存在")
...
(等等)

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

4

3 回答 3

4

就像是:

val batch = for{
  a <- safe(doA, "A failed") either
  b <- safe(doB, "B failed") either
  c <- safe(doC, "C failed") either
} yield(a,b,c)
batch fold( error(_), doSuccess(_) )

在 safe 执行的地方,您猜对了,安全(try/catch)操作接受失败(Left 结果)消息并返回 Either RightProjection(它允许您在遍历故障点错误消息时执行上述批处理操作)

class Catching[T](f: => T) {
  def either(msg: String) = {
    try { Right(f).right } catch { Left(msg).right }
  }
}
def safe[T](f: => T) = new Catching(f)

如果您想记录特定的错误类型,也可以将选项方法添加到 Catching 类,以及记录。

请参阅 Jason Zaugg 的右偏解决方案,以及该主题的 scala-debate 中的这个线程。目前还没有达成共识,但大多数 scala “重量级”似乎都赞成。

这种方法的一个限制是,如果您尝试向 for{} 块添加条件(如果 a = b),它将无法编译(因为默认的 Either 过滤器方法返回 Option)。解决方法是实现 filter 和 withFilter,返回 Either,我还没有弄清楚/做的事情(如果有人已经这样做了,请发布)

于 2012-06-24T15:57:33.670 回答
4

这里有一些有用的东西:

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然后你可以通过它的右投影使用一元组合

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将是一条错误消息。这个单子组合很快就失败了。您可以使用scalaz实现将累积所有失败的组合(例如,如果既不存在XDir也不YDir存在,您将看到两条消息)。在这种情况下,代码将如下所示: Validation

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
于 2012-06-24T22:38:19.433 回答
1

是的,您可以在没有 scalaz 的情况下使用验证,请参阅此处了解自包含实现: http ://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/chunk-xhtml/apa.html HTH

于 2012-06-25T08:55:53.300 回答