如何在Scala中编写此验证逻辑?

时间:2012-06-24 14:10:52

标签: scala

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

val xdir = System.getProperty("XDir")
if (xdir == null)
   error("No XDir") // log the error and exit

val ydir = System.getProperty("YDir") 
if (ydir == null)
   error("No YDir")

if (!new File(xdir).isDirectory)
   error("XDir is not a directory")

if (!new File(ydir).isDirectory)
   error("YDir is not a directory")

if (!new File(xdir).exists)
   error("XDir does not exis")

if (!new File(ydir).exists)
   error("YDir does not exist")
...
(and so on)

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

3 个答案:

答案 0 :(得分: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(_) )

安全执行a,您猜对了,安全(尝试/捕获)操作失败(左结果)消息并返回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关于right biasing Eitherthis thread的解决方案,以及关于该主题的scala辩论。目前还没有达成共识,但大多数scala“heavyies”似乎都赞成。

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

答案 1 :(得分: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的monadic组合
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的左侧将是错误消息。这种monadic组合快速失败。您可以使用 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

答案 2 :(得分:1)

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