Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/38.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
编写Scalaz验证_Scala_Scalaz - Fatal编程技术网

编写Scalaz验证

编写Scalaz验证,scala,scalaz,Scala,Scalaz,我希望使用Scalaz进行验证,并希望能够在不同的上下文中重用验证函数。顺便说一句,我对Scalaz完全陌生 假设我有这些简单的支票: def checkDefined(xs: Option[String]): Validation[String, String] = xs.map(_.success).getOrElse("empty".fail) def nonEmpty(str: String): Validation[String, String] = if (str.nonE

我希望使用Scalaz进行验证,并希望能够在不同的上下文中重用验证函数。顺便说一句,我对Scalaz完全陌生

假设我有这些简单的支票:

def checkDefined(xs: Option[String]): Validation[String, String] =
  xs.map(_.success).getOrElse("empty".fail)

def nonEmpty(str: String): Validation[String, String] =
  if (str.nonEmpty) str.success else "empty".fail

def int(str: String): Validation[String, Int] = ...
我喜欢能够编写验证,其中一个的输出被输入到另一个。我可以用
flatMap
或via来轻松理解,但感觉一定有更好的方法

for {
  v1 <- checkDefined(map.get("foo"))
  v2 <- nonEmpty(v1)
  v3 <- int(v2)
  v4 <- ...
} yield SomeCaseClass(v3, v4)

Scalaz专家有什么想法吗?

您可能想看看下面描述验证组合的:

  • 单子(即
    flatMap
  • 应用函子的两种方法(使用
    @
    遍历
  • 基本上,规则是这样的:通过单子合成是快速失败的。也就是说,此时您的计算将短路,并解决故障(e)。使用应用程序函子意味着您可以累积失败(可能用于web表单验证)-这是通过使用
    集合
    (它是
    半群
    )作为失败类型来实现的-Canconic示例使用
    非空列表

    关于
    验证
    还有其他有用的内容:

    val1 <+> val2    //Acts like an `orElse`
    val1 >>*<< val2  //Accumulates both successes and failures
    
    在这种情况下,它实际上不值得一个完整的方法:

    for {
      v1 <- map get "foo" toSuccess "Empty :-("
      v2 <- some(v1) filterNot (_.isEmpty) toSuccess "Empty :-("
      v3 <- (v2.parseInt.fail map (_.getMessage)).validation 
      v4 <- ...
    } yield SomeCaseClass(v3, v4)
    
    用于{
    
    v1除了@oxbow_lakes建议的解决方案外,您还可以使用Kleisli成分

    scala> import scalaz._, Scalaz._
    import scalaz._
    import Scalaz._
    
    scala> def f: Int => Validation[String, Int] = i => if(i % 2 == 0) Success(i * 2) else    Failure("Odd!")
    f: Int => scalaz.Validation[String,Int]
    
    scala> def g: Int => Validation[String, Int] = i => if(i > 0) Success(i + 1) else Failure("Not positive!")
    g: Int => scalaz.Validation[String,Int]
    
    scala> type Va[+A] = Validation[String, A]
    defined type alias Va
    
    scala> import Validation.Monad._
    import Validation.Monad._
    
    scala> kleisli[Va, Int, Int](f) >=> kleisli[Va, Int, Int](g)
    res0: scalaz.Kleisli[Va,Int,Int] = scalaz.Kleislis$$anon$1@4fae3fa6
    
    scala> res0(11)
    res1: Va[Int] = Failure(Odd!)
    
    scala> res0(-4)
    res2: Va[Int] = Failure(Not positive!)
    
    scala> res0(4)
    res3: Va[Int] = Success(9)
    

    类型为
    A=>M[B]
    的函数,其中
    M:Monad
    称为Kleisli箭头

    您可以使用
    =>
    运算符组合两个Kleisli箭头
    A=>M[B]
    B=>M[C]
    以获得一个箭头
    A=>M[C]
    。这称为Kleisli组合

    表达式
    kleisli(f)>=>kleisli(g)>=>kleisli(h)
    相当于(a表达式的
    x=>

    for {
      v1 <- checkDefined(map.get("foo"))
      v2 <- nonEmpty(v1)
      v3 <- int(v2)
      v4 <- someComputation()
    } yield SomeCaseClass(v3, v4)
    
    结果将是

     Validtion[NonEmptyList[String], SomeCaseClass] which is equal to ValidationNEL[String, SomeCaseClass]
    

    如果两个验证都失败,NonEmptyList将同时包含这两个验证

    我最近编写了一个简单的“框架”对于可组合的声明性验证,我最初是基于@missingfaktor的答案实现的,然而,在他提出的基础上,我添加了一个DSL,用于处理输入的任意大小的元组,这些元组被输入到匹配的算术函数中进行验证

    其用途如下:

    def非空[A]=(msg:String)=>Vali{A:Option[A]=>
    a、 toSuccess(msg)
    }
    def validIso2CountryCode=(msg:String)=>Vali{x:String=>
    IsoCountryCodes2to3.get(x).toSuccess(msg)
    }
    val postal=“12345”。有些
    val country=“GB”。一些
    val参数=(
    邮政的
    |>非空[字符串](“需要邮寄”),
    国家
    |>非空[字符串](“需要国家/地区”)
    >=>validIso2CountryCode(“国家/地区必须有效”)
    )
    //由于实现的泛型类型级别特性,参数类型推断在这里不起作用;欢迎进行任何改进!
    验证(参数){(邮政:字符串,国家:字符串)=>
    println(s“邮政:$postal,国家:$country”)
    }
    

    除了missingfaktor的答案外,您可能会注意到scalaz 7没有用于
    验证的
    Monad
    ,因为它的行为与
    应用
    实例不匹配。因此,您可以为
    验证
    定义
    绑定
    ,以及转换器,以方便:

    import scalaz.{Bind, Kleisli, Validation, Success, Failure}
    
    implicit def toKleisli[E, A, B](f: A => Validation[E, B]): Kleisli[Validation[E, ?], A, B] =
      Kleisli[Validation[E, ?], A, B](f)
    
    implicit def fromKleisli[E, A, B](f: Kleisli[Validation[E, ?], A, B]): A => Validation[E, B] = f.run
    
    implicit def validationBind[E] = new Bind[Validation[E, ?]] {
    
      def bind[A, B](fa: Validation[E, A])(f: (A) => Validation[E, B]): Validation[E, B] = {
        import Validation.FlatMap._
        fa.flatMap(f)
      }
    
      def map[A, B](fa: Validation[E, A])(f: (A) => B): Validation[E, B] = fa.map(f)
    }
    
    val parse: Option[String] => Validation[String, Int] = checkDefined _ >=> nonEmpty _ >=> int _
    
    println(parse(None)) // Failure(empty)
    println(parse(Some(""))) // Failure(empty)
    println(parse(Some("abc"))) // Failure(java.lang.NumberFormatException: For input string: "abc")
    println(parse(Some("42"))) // Success(42)
    

    那么“(x1 |@x2){(x1,x2)=>…}”呢虽然我不太确定确切的语法…看,实际上我现在打开了一个选项卡。这是一个很好的例子。我正在努力解决的问题是,我希望我的检查组合起来。也就是说,一个检查的输出应该是下一个检查的输入。就像你的例子中,当你在列表中有检查函数时,它们都会检查在同一个人的实例中,这与我尝试做的不同。您正在描述
    flatMap
    ;也就是说,您已经知道答案了!非常感谢!不,在这种情况下,它不需要完整的方法,但我需要在不同的上下文中验证相同的内容。例如,一个id本身和一个具有id的实体以及其他一些字段。我所说的“更好的方法”是指像你上面描述的那些看起来很酷的短方法,这对于我们来说并不明显,因为我们有一个命令式的背景,想要更具功能性等等。哈哈,我也有一个命令式的背景。我已经使用
    验证
    大约18个月了,我只是“发现”
    几个月前,当我让你上线的时候……什么是最好的方法来利用现有的验证并改变失败。这就是你在上面的示例中所做的方法吗
    checkLong(x).map(Some()).fail.map(=>“oops”).validation
    Oh对于部分应用的类型构造函数推理!@oxbow_lakes,这是Scala中最需要的东西之一。不幸的是,它似乎不在他们的短期列表中。正如@oxbow_lakes指出的,有一种短路方法和一种累积方法。这个例子就是短路方法。你怎么不知道呢如果你想累积失败的话,就可以完成他的任务?说真的。既然你能很好地回答快速失败的问题,你能读懂这个问题吗?@missingfakt或者你的例子对scalaz 7不再有效。没有验证。Monad对象了。你知道如何让kleisli-arrow合成与scalaz 7验证一起工作吗?呃,不,它不能。第一次验证的成功结果需要作为第二次验证的输入,因此应用程序函子在这里无法帮助您
    (checkDefined(map.get("foo")).liftFailNel |@| nonEmpty(v1)) {(v1, v2) =
        SomeCaseClass(int(v2), someComputation)
    }
    
     Validtion[NonEmptyList[String], SomeCaseClass] which is equal to ValidationNEL[String, SomeCaseClass]
    
    import scalaz.{Bind, Kleisli, Validation, Success, Failure}
    
    implicit def toKleisli[E, A, B](f: A => Validation[E, B]): Kleisli[Validation[E, ?], A, B] =
      Kleisli[Validation[E, ?], A, B](f)
    
    implicit def fromKleisli[E, A, B](f: Kleisli[Validation[E, ?], A, B]): A => Validation[E, B] = f.run
    
    implicit def validationBind[E] = new Bind[Validation[E, ?]] {
    
      def bind[A, B](fa: Validation[E, A])(f: (A) => Validation[E, B]): Validation[E, B] = {
        import Validation.FlatMap._
        fa.flatMap(f)
      }
    
      def map[A, B](fa: Validation[E, A])(f: (A) => B): Validation[E, B] = fa.map(f)
    }
    
    val parse: Option[String] => Validation[String, Int] = checkDefined _ >=> nonEmpty _ >=> int _
    
    println(parse(None)) // Failure(empty)
    println(parse(Some(""))) // Failure(empty)
    println(parse(Some("abc"))) // Failure(java.lang.NumberFormatException: For input string: "abc")
    println(parse(Some("42"))) // Success(42)