2013-02-18 16 views
5

Sono un principiante nel lavoro di programmazione funzionale e ho una sequenza di ValidationNEL [A, B] e vorrei accumulare gli errori in un nuovo ValidationNEL [A, B]. Questo dipende dal fatto che B è una struttura di dati mutabile proveniente dal codice legacy, e quindi sarebbe opportuno mantenere un Seq [B].Accumulare solo errori di validazione in Scalaz

So per altri posti che cumulando errori e il successo è possibile attraverso il metodo della sequenza: Processing a list of Scalaz6 Validation

Dalla mia comprensione tutto tratta di scrivere un applicativo corretto e forse un Traverse corretta.

trait MA[M[_], A] extends PimpedType[M[A]] with MASugar[M, A] { 

    def sequence[N[_], B](implicit a: A <:< N[B], t: Traverse[M], n: Applicative[N]): N[M[B]] = 
    traverse((z: A) => (z: N[B])) 

    def traverse[F[_],B](f: A => F[B])(implicit a: Applicative[F], t: Traverse[M]): F[M[B]] = 
    t.traverse(f, value) 
    } 

Come si avvia? Quando ho provato a cercare nel codice sorgente Scalaz per scoprire come implementare la mia Applicativo, mi sono estremamente confuso. Non ero nemmeno in grado di scoprire quale applicativo consente di accumulare sia i fallimenti che il successo in Validation.

+0

Stai cercando di passare da Seq [ValidtionNEL [A, B]] a ValidationNEL [A, Seq [B]] o qualcosa di simile? – Noah

+0

no da Seq [ValidtionNEL [A, B]] a ValidationNEL [A, B] – Edmondo1984

+0

Quindi ValidationNEL è solo Validation [NonEmptyList [A], B] quindi non puoi fare quello che chiedi a meno che tu non possa aggiungere B's insieme (int , elenchi, ecc.). – Noah

risposta

3

in ritardo alla festa, ma a partire da Scalaz 7.0.4, possiamo fare questo:

def takeLastSuccess[A, B](seq: Seq[ValidationNel[A, B]]) = { 
     implicit val useLast = Semigroup.lastSemigroup[B] 
     seq reduceLeft (_ +++ _) 
    } 
2

Ora che ho capito la tua domanda un po 'meglio, questo è piuttosto semplice:

def takeLastSuccess[A, B](seq:Seq[ValidationNEL[A, B]]) = 
    seq.sequence[({type l[a] = ValidationNEL[A, a]})#l, B].map(_.last) 

Quando si sequenziare questo Scala ha qualche problema con i tipi quindi è necessario utilizzare un tipo lambda. Sequence è una bella scorciatoia per passare da Seq [Something [X]] a Something [Seq [X]]. Infine, mappiamo il successo e otteniamo l'ultimo B dalla sequenza di B.

Andando fuori l'esempio da the post you cited, ecco quello che ottengo dalla REPL:

scala> import scalaz._, Scalaz._ 
import scalaz._ 
import Scalaz._ 

scala> type ExceptionsOr[A] = ValidationNEL[Exception, A] 
defined type alias ExceptionsOr 

scala> val successResults: Seq[ExceptionsOr[Int]] = Seq(
    |  "13".parseInt.liftFailNel, "42".parseInt.liftFailNel 
    | ) 
successResults: Seq[ExceptionsOr[Int]] = List(Success(13), Success(42)) 

scala> val failResults: Seq[ExceptionsOr[Int]] = Seq(
    |  "13".parseInt.liftFailNel, "a".parseInt.liftFailNel, "b".parseInt.liftFailNel 
    | ) 
failResults: Seq[ExceptionsOr[Int]] = List(Success(13), Failure(NonEmptyList(java.lang.NumberFormatException: For input string: "a")), Failure(NonEmptyList(java.lang.NumberFormatException: For input string: "b"))) 

scala> def takeLastSuccess[A, B](seq:Seq[ValidationNEL[A, B]]) = seq.sequence[({type l[a] = ValidationNEL[A, a]})#l, B].map(_.last) 
takeLastSuccess: [A, B](seq: Seq[scalaz.Scalaz.ValidationNEL[A,B]])scalaz.Validation[scalaz.NonEmptyList[A],B] 

scala> takeLastSuccess(successResults) 
res0: scalaz.Validation[scalaz.NonEmptyList[Exception],Int] = Success(42) 

scala> takeLastSuccess(failResults) 
res1: scalaz.Validation[scalaz.NonEmptyList[Exception],Int] = Failure(NonEmptyList(java.lang.NumberFormatException: For input string: "a", java.lang.NumberFormatException: For input string: "b")) 
+0

Questa è una soluzione simile a quella che ho già implementato. La soluzione ideale sarebbe quella di fornire un Applicativo corretto affinché il metodo della sequenza funzioni come desidero – Edmondo1984