首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >编写Scalaz验证

编写Scalaz验证
EN

Stack Overflow用户
提问于 2012-02-24 17:17:30
回答 5查看 4.5K关注 0票数 14

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

假设我有这些简单的检查:

代码语言:javascript
复制
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进行理解,但感觉一定有比这更好的方法。

代码语言:javascript
复制
for {
  v1 <- checkDefined(map.get("foo"))
  v2 <- nonEmpty(v1)
  v3 <- int(v2)
  v4 <- ...
} yield SomeCaseClass(v3, v4)

代码语言:javascript
复制
val x1 = checkDefined(map get "foo").flatMap(nonEmpty).flatMap(int)
val x2 = check(...)

// How to combine x1 and x2?

Scalaz专家有什么想法吗?

EN

回答 5

Stack Overflow用户

发布于 2012-02-24 18:21:33

除了@oxbow_lakes建议的解决方案外,您还可以使用克莱斯利组合。

代码语言:javascript
复制
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称为克莱斯利箭头。

您可以使用A => M[C]运算符组合两个克雷斯利箭头A => M[B]B => M[C],以获得箭头A => M[C]。这就是所谓的克莱斯利组合。

表达式kleisli(f) >=> kleisli(g) >=> kleisli(h)等同于x => for(a <- f(x); b <- g(a); c <- h(b)) yield c,去掉了不必要的本地绑定。

票数 17
EN

Stack Overflow用户

发布于 2012-02-24 18:01:32

您可能需要查看,它使用以下命令描述验证组合:

两种方式的flatMap)

  • Applicative函数式(即使用traverse)

  1. Monad

基本上,规则是这样的:通过monad进行组合是快速失败的。也就是说,您的计算将在这一点上短路并解析为Failure(e)。使用应用函数器意味着您可以累积失败(可能用于web表单验证)-这是通过使用collection (这是一个Semigroup)作为失败类型来实现的-规范示例使用NonEmptyList

Validation上还有其他有用的东西:

代码语言:javascript
复制
val1 <+> val2    //Acts like an `orElse`
val1 >>*<< val2  //Accumulates both successes and failures

在你的具体例子中,为什么你认为“肯定有更好的方法”,而不是通过for-comprehension?不过,它还可以稍微改进一下:

代码语言:javascript
复制
def checkDefined(xs: Option[String]) = xs.toSuccess("empty :-(")

在这种情况下,它实际上不值得使用完整的方法:

代码语言:javascript
复制
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)
票数 14
EN

Stack Overflow用户

发布于 2012-02-24 18:55:29

表达式

代码语言:javascript
复制
for {
  v1 <- checkDefined(map.get("foo"))
  v2 <- nonEmpty(v1)
  v3 <- int(v2)
  v4 <- someComputation()
} yield SomeCaseClass(v3, v4)

可以用这种方式替代

代码语言:javascript
复制
(checkDefined(map.get("foo")).liftFailNel |@| nonEmpty(v1)) {(v1, v2) =
    SomeCaseClass(int(v2), someComputation)
}

结果将会是

代码语言:javascript
复制
 Validtion[NonEmptyList[String], SomeCaseClass] which is equal to ValidationNEL[String, SomeCaseClass]

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9428174

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档