2013-07-17 18 views
6

Sto provando ad usare Scalaz 7 Validation nella mia app. Tuttavia, sto riscontrando un problema nel far funzionare il functor applicativo |@| per coalizzare i miei errori. Ecco il codice che ho:Validazione scalaz con funtore applicativo | @ | non funziona

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])(_ |@| _) 
} 

I minValidate e sizeValidate funzioni solo restituiscono ValidationResults.

Il problema è che questo codice non verrà compilato. Il messaggio di errore è:

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

Non ho idea di cosa significhi ... devo dare a Scala più informazioni di tipo?

Quello che sto cercando di realizzare è, se tutti i campi sono successNel s, quindi restituire, altrimenti, restituire una combinazione di tutti i failureNel s.

|@| è cambiato rispetto alla versione precedente di Scalaz? Perché anche se faccio qualcosa del tipo:

().successNel |@|().successNel 

Ho lo stesso errore.

Aggiornamento

ho iniziato rovistando la fonte Scalaz e ho trovato il +++ che sembra fare quello che voglio.

Qual è la differenza tra +++ e |@|?

risposta

10

La sintassi del builder applicativo di Scalaz (|@|) offre un modo per "sollevare" le funzioni in un funtore applicativo. Supponiamo di avere i seguenti risultati, ad esempio:

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 

Possiamo alzare la funzione lista di concatenazione (++) nella Validation in questo modo:

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

Questa sintassi è un po 'strano, è molto diverso come si elevano le funzioni in un functor applicativo in Haskell, ad esempio, ed è progettato in questo modo principalmente per superare in astuzia il sistema di inferenza di tipo abbastanza stupido di Scala. Vedi my answer here o blog post here per ulteriori discussioni.

Una parte della stranezza è che xs |@| ys non significa realmente nulla da solo: è essenzialmente un elenco di argomenti che è in attesa di essere applicato a una funzione che si eleverà nel suo functor applicativo e si applicherà a se stesso.

La +++ su Validation è una sorta di creatura molto più semplice: è solo l'operazione di addizione per l'istanza Semigroup per il tipo (si noti che si potrebbe equivalentemente usare di Scalaz dell'operatore semigruppo |+| qui al posto di +++). Gli dai due risultati Validation con i corrispondenti tipi di semigruppo e ti dà un altro Validation -non qualcosa di terribile ApplyOps.


Come nota laterale, in questo caso l'operazione di addizione per Validation s' semigruppo è lo stesso del funzionamento semigruppo per il lato destro sollevato in Validation:

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

Questo non sarà tuttavia, è sempre il caso (non è per \/, ad esempio, dove il semigruppo accumula errori ma non il funtativo applicativo).