2013-06-05 14 views
7

Mam 19 ciągów, które muszą być zatwierdzone na różne typy. Po pomyślnym sprawdzeniu poprawności chciałbym utworzyć instancję klasy reprezentującej wiersz arkusza kalkulacyjnego (gdzie kolumny nie mają tego samego typu).Scalaz: w jaki sposób mogę zebrać awarie lub zastosować funkcję walidacji z różnymi typami?

Jeśli jeden lub więcej ciągów nie zostanie sprawdzonych, chciałbym, aby błędy zostały zgromadzone w NonEmptyList.

Jeśli było 12 lub mniej przedmiotów, mógłbym użyć | @ | lub zastosuj12. Jeśli używam wyrażenia, to nie działa szybko i nie dochodzi do akumulacji.

Mogę sekwencjonować niepowodzenia, gdy wyrażenie for nie działa, ale to oznacza, że ​​robię pętlę dwa razy. Czy istnieje sposób użycia skalazu, aby wyciągnąć każdy sukces sprawdzania poprawności do zmiennej (jak by się stało, gdybym użył wyrażenia do utworzenia instancji klasy) w tym samym czasie, co gromadzenie wszystkich błędów?

+1

To trochę przylegające, ale można [używać '<*>' (lub 'ap') bezpośrednio] (http://stackoverflow.com/a/11502894/334519), które będą gromadzić błędy i nie mają arbitralne ograniczenie liczby przypadków, w których można je zastosować. –

+0

Nie można po prostu "odwzorować" listy łańcuchów na 'Validation', a następnie' partition' wynikową listę przez 'isFailure'. – cmbaxter

Odpowiedz

10

Załóżmy, że mamy klasę przypadku (co może mieć więcej niż dwunastu członków):

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

I że jesteśmy reprezentujących błędy jako ciągi i zdefiniowano typu alias dla wygody:

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

mamy też pewne wyniki walidacji

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 

teraz możemy napisać:

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))) 

co daje nam to, co chcemy:

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

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

W Haskell będzie to much prettier -you'd wystarczy napisać:

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

Scala słabszy typ wnioskowanie wymaga od nas napisać argumenty w niewłaściwej kolejności, niestety.

+0

To działa dla mnie, chociaż byłoby miło nie mieć floty bliskich parenów. :) –

+2

Zauważ, że napisałem [post na blogu] (http://meta.plasm.us/posts/2013/06/05/applicative-validation-syntax/) z niektórymi alternatywami za pomocą [Bezkształtne] (https) : //github.com/milessabin/shapeless). –

+0

Czy to jest seplenienie? – rethab

Powiązane problemy