Admito que el título no es muy explícito: lo siento por eso.Scalaz: validación en una comprensión y registro
Supongamos que tengo una para-comprensión:
for {v1<-Validation1(input)
v2<-Validation2(v1)
v3<-Validation3(v2)
} yield result
Validation1, Validation2 y Validation3 hacer algunas comprobaciones (por ejemplo "la edad> 18") y el uso fracaso/éxito; entonces, si algo está mal, la comprensión forzada aborta y obtengo la razón en la parte de falla del resultado, de lo contrario obtengo el valor esperado en la parte de éxito. Hasta ahora, tan bueno y nada muy difícil.
Pero Validation1, Validation2, Validation3 son exitosos si su entrada cumple algunas reglas (por ejemplo, "el hombre puede votar porque su edad es mayor a 18 y su nacionalidad es francesa"). lo que quiero es mantener un rastro de las reglas que se aplican para poder mostrarlas al final.
Es claramente un caso de uso del registro. pero yo dude sobre la manera de hacerlo:
tener un objeto "registrador" que es accesible por cualquier función (Validation1, 2 y 3, pero también la persona que llama que quiere mostrar el contenido del registro)
Hacer el registrador de un parámetro de Validation1, 2 y 3
Espere el capítulo pertinente de la "programación funcional en Scala" :)
Otro?
gracias por sus consejos
Editado el 10 de abril
Así, supongamos que quiero para calcular la función: x -> 1/sqrt (x)
En primer lugar, Calculo sqrt (x) comprobando que x> 0 y luego tomo el inverso si no es cero.
con scalaz.Validation, es simple:
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
Ahora, si éxitos squareRoot, quiero registrar el successsquaretoot cadena de Éxitos y si inversas, quiero registrar el successinverse cadena para que la función resultat acumula las cuerdas tanto en caso de éxito
empecé con ValidationT como yo Ocho sugirió:
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)))
Pero no puedo encontrar h cómo combinarlos en una comprensión forzosa. Además, para obtener el resultado de uno de ellos, tengo que escribir: squareroot2 (4) .run.corro que parece extraño y en la forma en que lo escribí, incluso en caso de fallo se registra el cadenas successsquareroot:
println(squareroot2(-1).run.run)
impresiones: (squareRoot bien, Fracaso (No se puede tomar raíz cuadrada de un número negativo))
¡Gracias! Benoit
Editado día 12 Abril
Así Yo Ocho sugirió este fragmento:
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
y me advirtió que algunas anotaciones de tipo era necesario. Efectivamente, el tpye de retorno de raíz cuadrada e inversa es bastante feo: ¡es una Validación de algo que tuve dificultades para entender!
Entonces, tuve que especificar el tipo de devolución explícitamente: def inversa (x: Doble): ValidaciónT [?, E, A] donde "E" es Cadena y "A" es Doble (¡eso fue fácil!). Pero, ¿y el primero? Debe ser una mónada (hasta donde yo entiendo) y elegí la más simple: Id (es decir, Identidad).
así que ahora tenemos:
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)
Pero el para-comprensión no se compila porque "y" no es un doble, pero un [, cadena, doble Id] Por otra parte, el primer mensaje registrado WriterT ("Squareroot ok") está "perdido".
el tiempo, me gustó que:
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)
que da:
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)
fresco! Sería mejor usar una lista [String] para el escritor, ¡pero creo que estoy en el buen camino!
Y ahora, se me ocurre para mis vacaciones (mañana!) :)
Editado el 14 de mayo
así, el código no se compila, pero el error está en Yo Ocho de última sugerencia (Tenga en cuenta que no es una ofensa otra vez Yo Ocho que es un modelo de bondad!). Os presento el código completo y el error:
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)
}
}
Aquí es la sesión de terminal:
[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
supongo que es algo que he echado de menos desde el principio que podría explicar por qué estoy pegado por algunas semanas!
Benoit
Modificado el 15 de mayo
La compilación del código, que tienen un primer error:
could not find implicit value for parameter F: scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias]
Después de algunos intentos, Reescribí la importación de esta manera:
import scalaz.Writer
import scalaz.std.string._
import scalaz.Id._
import scalaz.WriterT
import scalaz.ValidationT
import scala.Math._
Hay sti ll un error: se hizo presente con el código que escribió el 14 de mayo Obviamente
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
Este error, es difícil entender lo que iimport exactamente con scalaz y siete. Usando la versión 6, todo parecía más simple: uno solo tenía que importar scalaz._ y Scalaz._
Me siento como un "escritor de casa desesperado" :) (sí, estoy de acuerdo, no es muy astuto pero es relajante!)
Benoit
23 de de mayo de
Ouf! Funciona efectivamente con la última versión de scalaz-seven: tenga en cuenta que tuve que compilarlo en lugar de descargar una instantánea.
¡eso es genial!
Para aquellos que estén interesados, aquí está la salida:
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 Ocho, si por casualidad nos encontramos un día, te voy a pagar una cerveza!
Benoit
Creo que su versión de scalaz-seven no es buena. El código se está ejecutando correctamente con la última versión de scalaz-seven branch –
Me perdí tu publicación. Eso es lo que supuse. Estoy usando la instantánea del 14 de abril. Debería haber tenido tiempo para probar con la última versión esta noche – bhericher