2012-02-06 92 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和遍歷遊戲中有一些詭計,但我已經失去了靈感。