【问题标题】:How to code this validation logic in Scala?如何在 Scala 中编写此验证逻辑?
【发布时间】:2012-06-26 01:08:47
【问题描述】:

假设我想在 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 中编写此验证链的最佳方式是什么?

【问题讨论】:

    标签: scala


    【解决方案1】:

    这里有一些有用的东西:

    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 Validation 实现将累积所有失败的组合(例如,如果 XDir 和 YDir 都不存在,您将看到两条消息)。在这种情况下,代码将如下所示:

    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
    

    【讨论】:

    • 好吧,如果您通过自己的库引入相同的概念,您可以。您正在寻找的是 applicative functors 和 monoids 的组合。但是,这些非常有用和常见,您不妨只使用 scalaz。
    【解决方案2】:

    类似:

    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)操作,该操作接受失败(左结果)消息并返回 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 类添加一个选项方法,以及记录。

    也可以从 scala-debate 中查看 Jason Zaugg 对 right biasing Either 和 this thread 的解决方案。目前还没有达成共识,但大多数 scala “重量级”似乎都赞成。

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

    【讨论】:

    • 另请参阅来自 scalaz 的 Validation。
    • @mergeconflict 谢谢,还没有深入了解 Scalaz,我仍然在 Scala 本身中沾沾自喜。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多