2012-02-06 67 views
4

我有一个验证对象Scalaz验证,验证内值

val v = Validation[String, Option[Int]] 

我需要进行第二次的验证,以检查是否实际整数值是等于100例如。如果我做

val vv = v.map(_.map(intValue => if (intValue == 100) 
           intValue.success[String] 
          else 
           "Bad value found".fail[Integer])) 

我得到:

Validation[String, Option[Validation[String, Int]]] 

怎么可能让维维也为验证[字符串,选项[INT]]中最简洁的方式

====从我自己=====

找到可行的解决方案:

val validation: Validation[String, Option[Int]] = Some(100).success[String] 

val validatedTwice: Validation[String, Option[Int]] = validation.fold(
    _ => validation,        // if Failure then return it 
    _.map(validateValue _) getOrElse validation // validate Successful result 
) 

def validateValue(value: Int): Validation[String, Option[Int]] = { 
    if (value == 100) 
    Some(value).success[String] 
    else 
    "Bad value".fail[Option[Int]] 
} 

看起来不简洁大方,虽然它的工作原理

==============

从我自己的解决方案二,但看起来也过compicated:

val validatedTwice2: Validation[String, Option[Int]] = validation.flatMap(
    _.map(validateValue _).map(_.map(Some(_))) getOrElse validation) 

def validateValue(value: Int): Validation[String, Int] = { 
    if (value == 100) 
     value.success[String] 
    else 
     "Bad value".fail[Int] 
} 

回答

0

使用flatMap,就像这样:

v.flatMap(_.parseInt.fail.map(_.getMessage).validation) 
+0

为什么'parseInt'? – 2012-02-06 19:52:57

2

您的解决方案过于复杂。以下就够了!

v flatMap (_.filter(_ == 100).toSuccess("Bad value found")) 

toSuccess的来自OptionW并转换一个Option[A]Validation[X, A]服用设置用于在故障情况下,所述选项为空的情况下的值。该flatMap是这样的:

Validation[X, A] 
      => (A => Validation[X, B]) 
           => (via flatMap) Validation[X, B] 

也就是说,flatMap地图,然后压平(join在scalaz-说法):

Validation[X, A] 
      => (A => Validation[X, B]] 
          => (via map) Validation[X, Validation[X, B]] 
                => (via join) Validation[X, B] 
+1

嗨, 为V =一些(100).success [字符串]您的解决方案returnes成功(100),但我想获得成功(有些(100)) 并为V = None.success [字符串]您的解决方案返回失败(找到错误值),但我希望看到成功(无) 更多说明为什么我需要这个:我从数据库验证中获取,其中包含数据访问错误或成功载入的数据。数据实际上可能不存在于数据库中,并且不是错误。如果数据存在,我想验证它的一些规则。 – 2012-02-06 20:49:41

2

首先,让我们设置一些类型别名,因为重复输入了这一点,将老得很快。当我们在这里时,我们会整理一下验证逻辑。

type V[X] = Validation[String, X] 
type O[X] = Option[X] 
def checkInt(i: Int): V[Int] = Validation.fromEither(i != 100 either "Bad value found" or i) 

val v: V[O[Int]] = _ 

,这是我们已经开始了 - B1相当于你VV情况

val b1: V[O[V[Int]]] = v.map(_.map(checkInt)) 

让我们测序,以翻转在V【O [V [INT]]进入选项为V [V【O [INT]]

val b2: V[V[O[Int]]] = v.map(_.map(checkInt)).map(_.sequence[V, Int]) 

,或者如果你感觉的λ-Y也有可能是

sequence[({type l[x] = Validation[String, x]})#l, Int] 

接下来我们展示了嵌套验证 - 我们将引入Validation monad,因为实际上我们确实希望在这里使用fastfail行为,尽管这通常不是正确的做法。

implicit val monad = Validation.validationMonad[String] 
val b3: V[O[Int]] = v.map(_.map(checkInt)).map(_.sequence[V, Int]).join 

所以,现在我们已经有了一个验证[字符串,选项[INT]],所以我们在那里,但是这仍然是相当混乱。让我们用一些等式推理整理一下

通过我们所知道的第二个仿函数法是:

X.map(_.f).map(_.g) = X.map(_.f.g) => 
    val i1: V[O[Int]] = v.map(_.map(checkInt).sequence[V, Int]).join 

,并通过一个单子的定义:

X.map(f).join = X.flatMap(f) => 
    val i2: V[O[Int]] = v.flatMap(_.map(checkInt).sequence[V, Int]) 

,然后我们应用免费遍历定理:
(我在那张血腥的纸上挣扎着这么多,但看起来好像有些陷入了!):

X.map(f).sequence = X.traverse(f andThen identity) = X.traverse(f) => 
    val i3: V[O[Int]] = v.flatMap(_.traverse[V, Int](checkInt)) 

所以现在我们正在寻找一些更文明的东西。我想,在flatMap和遍历游戏中有一些诡计,但我已经失去了灵感。