2012-06-10 6 views
7

Ich schreibe eine vollständig asynchrone Bibliothek für den Zugriff auf einen Remote-Dienst (mit Play2.0), ich verwende Promise und Validation, um nicht blockierende Anruf zu erstellen, der einen Typ hat, der fehlschlägt und gültiges Ergebnis sofort.Async-Berechnung mit Validierung in Scala mit Scalaz

Promise kommt von Play2-scala, wo Validation von scalaz kommt.

Also hier ist die Art von Beispielen für solche Funktionen

  • f :: A => Promise[Validation[E, B]]
  • g :: B => Promise[Validation[E, C]]

So weit, so gut, jetzt, wenn ich möchte, dass sie komponieren , Kann ich einfach die Tatsache verwenden, dass Promise eine flatMap präsentieren, so kann ich es mit einem Verständnis für die

for (
    x <- f(a); 
    y <- g(b) 
) yield y 
01 tun

Ok, ich habe hier eine Abkürzung zu meinem Problem genommen, weil ich die Validation Ergebnisse innerhalb des Verständnisses nicht wiederverwendet habe. Also, wenn ich x in g wiederverwendet werden soll, ist hier, wie ich genug

for (
    x <- f(a); // x is a Validation 
    y <- x.fold(
     fail => Promise.pure(x), 
     ok => g(ok) 
    ) 
) yield y 

Messe tun könnte, aber diese Art von vorformulierten wird meinen Code über verschmutzen und immer wieder gehen. Das Problem hier ist, dass ich eine Art zweistufige Monadische Struktur wie M[N[_]] habe.

In diesem Stadium gibt es eine Struktur in f ° Programmierung, die leicht die secong Ebene durch Überspringen mit einer solchen Struktur arbeiten ermöglicht:

for (
    x <- f(a); //x is a B 
    y <- g(b) 
) yield y 

nun unten ist, wie ich etwas Ähnliches erreicht.

Ich habe Art von monadische Struktur, die auf zwei Ebenen in einem Wraps, lassen ValidationPromised sagen, die die Promise Typ mit zwei Methoden gepimpt:

def /~> [EE >: E, B](f: Validation[E, A] => ValidationPromised[EE, B]): ValidationPromised[EE, B] = 
    promised flatMap { valid => 
     f(valid).promised 
    } 

def /~~>[EE >: E, B](f: A => ValidationPromised[EE, B]): ValidationPromised[EE, B] = 
    promised flatMap { valid => 
     valid.fold (
      bad => Promise.pure(KO(bad)), 
      good => f(good).promised 
     ) 
    } 

Das bin ich solche Dinge

 endPoint.service /~~>         //get the service 
     (svc =>             //the service 
     svc.start /~~> (st =>         //get the starting elt 
      svc.create(None) /~~>        //svc creates a new elt 
      (newE =>           //the created one 
      newEntry.link(st, newE) /~~>      //link start and the new 
      (lnk => Promise.pure(OK((st, lnk, newE))))  //returns a triple => hackish 
     ) 
     ) 
    ) 
tun können

Wie wir sehen können /~~> ist ziemlich ähnlich zu flatMap aber überspringt eine Ebene. Das Problem ist die Ausführlichkeit (deshalb gibt es "For-Comprehension" in Scala und "Do" in Haskell).

Ein weiterer Punkt, habe ich die /~>, die auch wie ein map steht, sondern arbeitet auf der zweiten Ebene (anstelle des gültigen Typ - dritte Ebene)

So ist meine zweite Frage ist logische Folge der früheren ... stimme ich mit dieser Konstruktion einer nachhaltigen Lösung zu?

traurig sein, dass lange

+0

Ich wollte ScalaZ jetzt schon eine Weile mit meinen Play-Apps verwenden, und das ist ein guter Anstoß für mich. Ich werde Sie wissen lassen, wie es mir geht, und ich werde hoffentlich in der Lage sein, hier eine sinnvolle Antwort zu geben. – opyate

+0

Ja! Danke. Eigentlich liegt das eigentliche Problem nicht in der Verwendung von ScalaZ mit Play. Es ist eine allgemeinere Frage (über f ° prog), denn ohne Play (also nur ScalaZ) hätte ich 'IO' anstelle von' Promise' verwendet. Daher würde ich das gleiche Muster haben, das heißt: 'IO [Validation [E, A]]' –

Antwort

4

Das Konzept Sie hier monad transformers suchen sind. Kurz gesagt, Monod-Transformatoren kompensieren monads not composing, indem Sie sie "stapeln" können.

Sie haben die Version von Scalaz, die Sie verwenden, nicht erwähnt, aber wenn Sie in der scalaz-seven branch suchen, finden Sie ValidationT. Dies kann verwendet werden, um alle F[Validation[E, A]] in eine ValidationT[F, E, A], wo in Ihrem Fall F = Promise. Wenn Sie f und g ändern ValidationT zurück, dann können Sie Ihren Code als

for { 
    x ← f(a) 
    y ← g(b) 
} yield y 

verlassen Dies wird Ihnen ein ValidationT[Promise, E, B] als Ergebnis.

+0

MMmh aussehen die Art von was ich suche !! Ok, also versuche ich zuerst mit dem Monad Transformer mein Selbst, aus zwei Gründen, weil es die einzige Art zu lernen ist, und zweitens, weil ich in der 6.0.4 Branche bin. Und dann werde ich wahrscheinlich auf den 7. umziehen und für den ValidatorT umgestalten ... In allen Fällen werde ich zurückkommen, um mehr zu erzählen. Danke nochmal –

+0

Ok. Großer Doc. Ich habe mir die 'ValidationT' angesehen, ihre' flatMap' Methode ist in der Tat das, was ich brauche (und es ist dasselbe wie mein '/ ~~>' one). Aber um es direkt zu verwenden, benötigt 'Promise' eine TypeClass-Instanz von' Monad', die es nicht gibt. Wenn ScalaZ herauskommt, werde ich es implementieren, um 'ValidationT' direkt verwenden zu können. –