Ammetto che il titolo non è molto esplicito: mi dispiace per quello.Scalaz: convalida di una comprensione e registrazione
Si supponga Ho un per-di comprensione:
for {v1<-Validation1(input)
v2<-Validation2(v1)
v3<-Validation3(v2)
} yield result
Validation1, Validation2 e Validation3 fare qualche controllo (per esempio "età> 18") e l'uso fallire/successo; quindi se qualcosa è sbagliato, la comprensione incomprensibile e io ricevo il motivo nella parte del fallimento del risultato, altrimenti ottengo il valore atteso nella parte di successo. Fin qui, tutto bene e niente di molto difficile.
Ma Validation1, Validation2, Validation3 hanno successo se il loro input soddisfa alcune regole (ad esempio "il ragazzo può votare perché la sua età è maggiore di 18 e la sua nazionalità è francese"). quello che voglio è tenere traccia delle regole che vengono applicate per poterle mostrare alla fine.
È chiaramente un caso di utilizzo della registrazione. ma esita sul modo per farlo:
avere un oggetto "logger", che è accessibile da qualsiasi funzione (Validation1, 2 e 3, ma anche il chiamante che vuole visualizzare il contenuto del registro)
Fai il logger un parametro di Validation1, 2 e 3
Attendere il capitolo pertinente di "programmazione funzionale in Scala" :)
Altro?
grazie per il vostro consigli
Modificato il 10 aprile
Così, supponiamo che io voglio calcolare la funzione: x -> 1/sqrt (x)
In primo luogo, Calcolo sqrt (x) controllando che x> 0 e poi prendo l'inverso se non zero.
con scalaz.Validation, è semplice:
val failsquareroot= "Can't take squareroot of negative number"
val successsquareroot= "Squareroot ok"
val failinverse="Can't take inverse of zero"
val successinverse= "Inverse ok"
def squareroot(x:Double)=if (x < 0) failsquareroot.fail else sqrt(x).success
def inverse(x:Double)= if (x == 0) failinverse.fail else (1/x).success
def resultat(x:Double)= for {
y <- squareroot(x)
z<-inverse(y)
} yield z
Ora, se i successi SQUAREROOT, voglio registrare il successsquaretoot corda e se sucesses inverse, voglio registrare il successinverse stringa in modo che la funzione resultat accumula le stringhe sia in caso di successo
ho iniziato con ValidationT come Yo Otto suggerito:
def squareroot2(x:Double)=ValidationT[({type f[x] = Writer[String,x]})#f, String,Double](Writer(successsquareroot,squareroot(x)))
def inverse2(x:Double)=ValidationT[({type f[x] = Writer[String,x]})#f, String,Double](Writer(successinverse,inverse(x)))
Ma io non riesco a trovare h dobbiamo combinarli in una comprensione preliminare. Inoltre, per ottenere il risultato di uno di questi, devo scrivere: squareroot2 (4) .run.Corro che sembra strano e nel modo che ho scritto, anche in caso di guasto della stringhe successsquareroot viene registrato:
println(squareroot2(-1).run.run)
stampe: (SQUAREROOT ok, Fallimento (Non può prendere radice quadrata di un numero negativo))
Grazie! Benoit
Modificato il 12 aprile
Così Yo Otto suggerito questo frammento:
def squareroot(x:Double) = if (x < 0) failureT("Can't take squareroot of negative number") else successT(sqrt(x))
def inverse(x:Double) = if (x == 0) failureT("Can't take inverse of zero ") else successT(1/x)
for {
y <- squareroot(x).flatMapF(i => Writer("Squareroot ok", i))
z <- inverse(y).flatMapF(i => Writer("Inverse ok", i))
} yield z
e mi ha avvertito che alcune annotazioni di tipo era necessario. Effettivamente, il ritorno di squareroot e inverso è piuttosto brutto: è una convalida di qualcosa che ho avuto difficoltà a capire!
Quindi, ho dovuto specificare esplicitamente il tipo di ritorno: def inverso (x: Double): ValidationT [?, E, A] dove "E" è String e "A" è Double (che era facile!). Ma per quanto riguarda il primo? Deve essere una monade (per quanto ho capito) e ho scelto il più semplice: Id (che è Identità).
Così ora abbiamo:
def squareroot(x:Double):ValidationT[Id,String,Double]=if (x < 0) failureT(failsquareroot) else successT(sqrt(x))
def inverse(x:Double):ValidationT[Id,String,Double]=if (x == 0) failureT(failinverse)else successT(1/x)
Ma la for-comprensione non compila perché "y" non è un doppio, ma un WriterT [Id, String, Double] Inoltre, il primo messaggio registrato ("Squareroot ok") è "perso".
Alla fine, ho fatto così:
def resultat(x:Double) = for {
y <- squareroot(x).flatMapF(i => Writer("Squareroot ok", i))
z <- inverse(y.run._2).flatMapF(i => Writer(y.run._1 + ", Inverse ok", i))
} yield z.run //Note that writing "z.run.run" doesn't compile
println("0 : " + resultat(0.0).run)
println("-1 : " +resultat(-1.0).run)
println("4 : " + resultat(4).run)
che dà:
0 : Failure(Can't take inverse of zero)
-1 : Failure(Can't take squareroot of negative number)
4 : Success((Squareroot ok, Inverse ok,0.5)
Cool! Sarebbe meglio usare una List [String] per il writer, ma penso di essere sulla buona strada!
E ora, posso pensare a mie vacanze (domani!) :)
Redatta il 14 maggio
bene, il codice non si compila, ma l'errore è in Yo Eight della scorsa suggerimento (nota che non è più un'offesa Yo Eight che è un modello di gentilezza!). Vi sottopongo il codice completo e l'errore:
import scala.math._
import scalaz._
import Scalaz._
object validlog extends ValidationTFunctions {
val failsquareroot= "Can't take squareroot of negative number"
val successsquareroot= "Squareroot ok"
val failinverse="Can't take inverse of zero"
val successinverse= "Inverse ok"
case class MyId[A](v: A)
implicit val myIdPointed = new Pointed[MyId]{
def point[A](v: => A) = MyId(v)
}
implicit def unId[A](my: MyId[A]): A = my.v
def squareroot(x:Double):ValidationT[({type f[x] = WriterT[MyId,String, x]})#f,String,Double]=if (x < 0) failureT[({type f[x] = WriterT[MyId,String, x]})#f,String,Double](failsquareroot) else successT[({type f[x] = WriterT[MyId,String, x]})#f,String,Double](sqrt(x))
def inverse(x:Double):ValidationT[({type f[x] = WriterT[MyId, String, x]})#f,String,Double]=if (x == 0) failureT[({type f[x] = WriterT[MyId,String, x]})#f,String,Double](failinverse) else successT[({type f[x] = WriterT[MyId,String, x]})#f,String,Double](1/x)
/* def resultat(x:Double) = for {
y <- squareroot(x).flatMapF(i => Writer(", Squareroot ok", i))
z <- inverse(y).flatMapF(i => Writer(", Inverse ok", i))
} yield z */
def main(args: Array[String]): Unit = {
println(inverse(0.0).run)
println(inverse(0.5).run)
println(squareroot(-1.0).run)
println(inverse(4.0).run)
}
}
Ecco la sessione del terminale:
[email protected]:~$ cd scala
[email protected]:~/scala$ scala -version
Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL
[email protected]:~/scala$ scala -cp ./scalaz7/scalaz-core_2.9.2-7.0-SNAPSHOT.jar validlog.scala
/home/benoit/scala/validlog.scala:15: error: object creation impossible, since method map in trait Functor of type [A, B](fa: Main.MyId[A])(f: A => B)Main.MyId[B] is not defined
implicit val myIdPointed = new Pointed[MyId]{
^
one error found
Credo che ci sia qualcosa che ho perso fin dall'inizio che potrebbe spiegare il motivo per cui io sono incollato per alcune settimane!
Benoit
Modificato il 15 maggio
Compilare il codice, ho un primo errore:
could not find implicit value for parameter F: scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias]
Dopo alcuni tentativi, ho riscritto l'importazione in questo modo:
import scalaz.Writer
import scalaz.std.string._
import scalaz.Id._
import scalaz.WriterT
import scalaz.ValidationT
import scala.Math._
C'è sti ll un errore:
error: could not find implicit value for parameter F: scalaz.Monad[[x]scalaz.WriterT[[+X]X,String,x]]
y <- squareroot(x).flatMapF(i => Writer("Squareroot ok", i))
^
one error found
Questo errore era presente con il codice che hai scritto il 14 maggio Ovviamente, è difficile capire che cosa esattamente iimport con scalaz-sette. Usando la versione 6, le cose sembravano più semplici: basta importare scalaz._ e Scalaz._
Mi sento come una "casalinga disperata" :) (sì, sono d'accordo, non è molto astuto ma è rilassante!)
Benoit
23 maggio
Ouf! Funziona in modo efficace con l'ultima versione di scalaz-seven: nota che ho dovuto costruirla invece di scaricare un'istantanea.
è fantastico!
Per coloro che sono interessati, ecco l'output:
0 : (Squareroot ok,Failure(Can't take inverse of zero))
-1 : (,Failure(Can't take squareroot of negative number))
4 : (Squareroot ok, Inverse ok,Success(0.5))
Yo Otto, se per caso ci incontriamo un giorno, ti pagherò una birra!
Benoit
Penso che la tua versione di scalaz-seven non sia buona. Il codice funziona correttamente con l'ultima versione del ramo scalaz-seven –
Ho perso il tuo post. Questo è quello che ho indovinato. Sto usando l'istantanea del 14 aprile. Avrei dovuto avere il tempo di provare l'ultima versione questa sera – bhericher