2013-06-05 48 views
7

我有19個字符串需要驗證成各種類型。當所有的驗證都成功的時候,我想實例化一個表示一行電子表格的類(其中列不全都具有相同的類型)。Scalaz:我如何積累故障或將功能應用於不同類型的驗證?

當一個或多個字符串無法驗證時,我想在NonEmptyList中累積錯誤。

如果有12個或更少的項目,我可以使用| @ |或申請12。如果我使用表達式,它會快速失敗並且不會發生累積。

當for表達式失敗時,我可以對失敗進行排序,但這意味着我循環了兩次。有沒有辦法使用scalaz把每個驗證成功都放到一個變量中(如果我用一個for表達式來實例化這個類的話會發生什麼情況),同時積累了所有的失敗?

+1

這有點笨拙,但你可以[直接使用'<*>'(或'ap')](http://stackoverflow.com/a/11502894/334519),這會積累錯誤,並且沒有它可以應用的次數的任意限制。 –

+0

難道你不能只將'字符串列表'映射到'驗證',然後''由'isFailure'分區結果列表。 – cmbaxter

回答

10

假設我們有一個案例類(其中可能有超過十二名成員):

case class Foo(a: Int, b: Char, c: Symbol, d: String) 

而且,我們正在代表錯誤字符串和定義一個類型別名爲方便:

type ErrorOr[A] = ValidationNel[String, A] 

我們也有一些驗證結果:

val goodA: ErrorOr[Int] = 1.success 
val goodB: ErrorOr[Char] = 'a'.success 
val goodC: ErrorOr[Symbol] = 'a.success 
val goodD: ErrorOr[String] = "a".success 

val badA: ErrorOr[Int] = "x".failNel 
val badC: ErrorOr[Symbol] = "y".failNel 

現在我們可以這樣寫:

val foo = (Foo.apply _).curried 

val good: ErrorOr[Foo] = goodD <*> (goodC <*> (goodB <*> (goodA map foo))) 
val bad: ErrorOr[Foo] = goodD <*> (badC <*> (goodB <*> (badA map foo))) 

這給了我們什麼,我們想:

scala> println(good) 
Success(Foo(1,a,'a,a)) 

scala> println(bad) 
Failure(NonEmptyList(x, y)) 

在Haskell,這將是much prettier -you'd只是寫:

Foo <$> goodA <*> goodB <*> goodC <*> goodD 

Scala的弱類型推斷要求我們寫不幸的是,這些論點的順序錯誤。

+0

這適用於我,雖然它不會有一個密切的parens艦隊。 :) –

+2

請注意,我已經使用[無形](https)寫了[一篇博客文章](http://meta.plasm.us/posts/2013/06/05/applicative-validation-syntax/) ://github.com/milessabin/shapeless)。 –

+0

這是一個lisp嗎? – rethab