2013-07-17 8 views
6

Próbuję użyć sprawdzania poprawności Scalaz 7 w mojej aplikacji. Jednak mam problem z pobieraniem funktora aplikacyjnego |@|, aby połączyć moje niepowodzenia. Oto kod mam:Scalaz Walidacja za pomocą funktora aplikacyjnego | @ | nie działa

type ValidationResult = ValidationNel[String, Unit] 

def validate[A: ClassTag](instance: A, fieldNames: Option[Seq[String]] = None): ValidationResult = { 
    val fields = classTag[A].runtimeClass.getDeclaredFields 
    val fieldSubset = fieldNames match { 
     case Some(names) => fields.filter { field => names.contains(field.getName) } 
     case None => fields 
    } 
    fieldSubset.map { 
     field => field.getAnnotations.toSeq.map { 
      field.setAccessible(true) 
      val (name, value) = (field.getName, field.get(instance)) 
      field.setAccessible(false) 
      annotation => annotation match { 
       case min: Min => minValidate(name, value, min.value()) 
       case size: Size => sizeValidate(name, value, size.min(), size.max()) 
      } 
     } 
    }.flatten[ValidationResult].foldLeft(().successNel[String])(_ |@| _) 
} 

W minValidate i sizeValidate funkcje po prostu wrócić ValidationResults.

Problem polega na tym, że ten kod nie zostanie skompilowany. Komunikat o błędzie:

Type mismatch, expected F0.type#M[NotInferedB], actual: ValidationResult 

Nie mam pojęcia, co to znaczy ... czy muszę podać Scala więcej informacji o typie?

Co staram się osiągnąć, jeśli wszystkie pola są successNel s, następnie zwrócić, w przeciwnym razie, zwrócić połączenie wszystkich failureNel s.

Czy zmieniono |@| od poprzedniej wersji programu Scalaz? Ponieważ, nawet jeśli robię coś takiego:

().successNel |@|().successNel 

Otrzymuję ten sam błąd.

Aktualizacja

zacząłem grzebać źródła Scalaz i znalazłem +++ który wydaje się robić to, co chcę.

Jaka jest różnica między +++ i |@|?

Odpowiedz

10

Składowa aplikacja budująca Scalaz (|@|) daje ci możliwość "podnoszenia" funkcji w funktor aplikacyjny. Załóżmy, że mamy następujące wyniki, na przykład:

val xs: ValidationNel[String, List[Int]] = "Error!".failNel 
val ys: ValidationNel[String, List[Int]] = List(1, 2, 3).success 
val zs: ValidationNel[String, List[Int]] = List(4, 5).success 

Możemy podnieść funkcję lista konkatenacji (++) do Validation tak:

scala> println((ys |@| zs)(_ ++ _)) 
Success(List(1, 2, 3, 4, 5)) 

scala> println((xs |@| ys)(_ ++ _)) 
Failure(NonEmptyList(Error!)) 

scala> println((xs |@| xs)(_ ++ _)) 
Failure(NonEmptyList(Error!, Error!)) 

składnia To trochę dziwne, że to bardzo przeciwieństwie jak na przykład podnosić funkcje do funktora aplikacyjnego w Haskell i zaprojektowano go w ten sposób przede wszystkim do przechytrzenia dość głupiego systemu wnioskowania typu Scala. Więcej informacji można znaleźć na stronie my answer here lub blog post here.

Jedną z części dziwności jest to, że xs |@| ys tak naprawdę nic nie znaczy - jest to w istocie lista argumentów, która czeka na zastosowanie do funkcji, którą podniesie do swojego funktora aplikacyjnego i zastosuje się do niego.

+++ na Validation jest znacznie prostszy rodzaj stworzeń, to tylko operacja dodatek dla instancji Semigroup dla danego typu (zauważ, że można równoważnie używać Scalaz za półgrupa operatora |+| tutaj zamiast +++). Dajesz mu dwa wyniki: Validation z pasującymi typami półgrupy i daje ci to inną Validation -nie straszną rzecz: ApplyOps.


Na marginesie, w tym przypadku operacja dodatek do Validation „s półgrupa jest taka sama jak operacja półgrupa dla prawej strony podnoszone do Validation:

scala> (xs |+| ys) == (xs |@| ys)(_ |+| _) 
res3: Boolean = true 

to nie będzie jednak zawsze tak jest (na przykład nie jest to dla \/, gdzie półgrupa gromadzi błędy, ale funktor aplikacyjny tego nie robi).

Powiązane problemy