Note that there are some explanatory texts on larger screens.

plurals
  1. POScalaz : validating in a for-comprehension and logging
    primarykey
    data
    text
    <p>I admit that the title is not very explicit : sorry for that.</p> <p>Assume I have a for-comprehension :</p> <pre><code>for {v1&lt;-Validation1(input) v2&lt;-Validation2(v1) v3&lt;-Validation3(v2) } yield result </code></pre> <p>Validation1, Validation2 and Validation3 do some checking (e.g "age > 18") and use fail/success ; so if something is wrong, the for-comprehension aborts and I get the reason in the failure part of the result, else I get the expected value in the success part. So far, so good and nothing very difficult.</p> <p>But Validation1, Validation2, Validation3 are successfull if their input satisfies some rules (e.g : "the guy can vote because his age is greater than 18 and his nationality is French") . what I want is to keep trace of the rules that are applied in order to be able to display them at the end.</p> <p>It's clearly a use case of logging. but I hesitate on the way to do it :</p> <ol> <li><p>Have an object "logger" that is accessible by any function (Validation1, 2 and 3 but also the caller that wants to display the content of the log)</p></li> <li><p>Make the logger a parameter of Validation1, 2 and 3</p></li> <li><p>Wait for the pertinent chapter of "Functional programming in Scala" :)</p></li> <li><p>Other?</p></li> </ol> <p>Thank for your advices</p> <p><strong>Edited on april 10</strong></p> <p>So, suppose I want to compute the function : x -> 1/sqrt(x)</p> <p>First, I compute sqrt(x) by checking that x > 0 and then I take the inverse if not zero.</p> <p>with scalaz.Validation, it is simple :</p> <pre><code>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 &lt; 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 &lt;- squareroot(x) z&lt;-inverse(y) } yield z </code></pre> <p>Now, if squareroot successes, I want to log the string successsquaretoot and if inverse sucesses, I want to log the string successinverse so that the function resultat accumulates the both strings in case of success</p> <p>I started with ValidationT as Yo Eight suggested :</p> <pre><code> 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))) </code></pre> <p>But I can't find how to combine them in a for-comprehension. Furthermore, to get the result of one of them, I have to write : squareroot2(4).run.run which seems strange and in the way I wrote it, even in case of failure the strings successsquareroot is logged :</p> <pre><code> println(squareroot2(-1).run.run) </code></pre> <p>prints : (Squareroot ok,Failure(Can't take squareroot of negative number))</p> <p>Thank you! Benoit</p> <p><strong>Edited on april 12</strong></p> <p>So Yo Eight suggested this snippet :</p> <pre><code> def squareroot(x:Double) = if (x &lt; 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 &lt;- squareroot(x).flatMapF(i =&gt; Writer("Squareroot ok", i)) z &lt;- inverse(y).flatMapF(i =&gt; Writer("Inverse ok", i)) } yield z </code></pre> <p>and he warned me that some type annotations was necessary. Effectivly, the return tpye of squareroot and inverse is rather ugly : it's a ValidationT of something that I had difficulties to understand!</p> <p>So, I had to specify the return type explictly : def inverse(x:Double) : ValidationT[?,E,A] where "E" is String and "A" is Double (that was easy!). But what about the first one? It must be a monad (as far as I understand) and I choosed the simpliest : Id (that is Identity).</p> <p>So now we have :</p> <pre><code> def squareroot(x:Double):ValidationT[Id,String,Double]=if (x &lt; 0) failureT(failsquareroot) else successT(sqrt(x)) def inverse(x:Double):ValidationT[Id,String,Double]=if (x == 0) failureT(failinverse)else successT(1/x) </code></pre> <p>But the for-comprehension doesn't compile because "y" is not a Double but a WriterT[Id, String, Double] Furthermore, the first logged message ("Squareroot ok") is "lost". </p> <p>Eventually, I did like that :</p> <pre><code> def resultat(x:Double) = for { y &lt;- squareroot(x).flatMapF(i =&gt; Writer("Squareroot ok", i)) z &lt;- inverse(y.run._2).flatMapF(i =&gt; 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) </code></pre> <p>which gives :</p> <pre><code> 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) </code></pre> <p>Cool! I would be better to use a List[String] for the Writer, but I think that I'm on the good way!</p> <p>And now, I can think to my holidays (tomorrow!) :)</p> <p><strong>Edited on may 14</strong></p> <p>well, the code doesn't compile, but the error is in Yo Eight's last suggestion (Note that it is not an offense again Yo Eight who is a model of kindness!) . I submit you the full code and the error : </p> <pre><code>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: =&gt; 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 &lt; 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 &lt;- squareroot(x).flatMapF(i =&gt; Writer(", Squareroot ok", i)) z &lt;- inverse(y).flatMapF(i =&gt; 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) } } </code></pre> <p>Here is the terminal's session :</p> <pre><code>benoit@benoit-laptop:~$ cd scala benoit@benoit-laptop:~/scala$ scala -version Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL benoit@benoit-laptop:~/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 =&gt; B)Main.MyId[B] is not defined implicit val myIdPointed = new Pointed[MyId]{ ^ one error found </code></pre> <p>I guess there is something that I've missed from the beginning that could explain why I'm sticked for some weeks!</p> <p>Benoit</p> <p><strong>Edited on may 15</strong></p> <p>Compiling your code, I have a first error :</p> <pre><code> could not find implicit value for parameter F: scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias] </code></pre> <p>After some tries, I rewrote the import in this manner :</p> <pre><code>import scalaz.Writer import scalaz.std.string._ import scalaz.Id._ import scalaz.WriterT import scalaz.ValidationT import scala.Math._ </code></pre> <p>There is still one error :</p> <pre><code> error: could not find implicit value for parameter F: scalaz.Monad[[x]scalaz.WriterT[[+X]X,String,x]] y &lt;- squareroot(x).flatMapF(i =&gt; Writer("Squareroot ok", i)) ^ one error found </code></pre> <p>This error was present with the code you wrote on may 14. Obviously, it is difficult to understand what to iimport exactly with scalaz-seven. Using the version 6, things looked simpler : one just had to import scalaz._ and Scalaz._ </p> <p>I feel like a "desperate housewriter" :) (yes, I agree, it is not very astute but it's relaxing!)</p> <p>Benoit</p> <p><strong>May 23</strong></p> <p>Ouf! It effectively works with the last version of scalaz-seven : note that I had to build it instead of downloading a snapshot.</p> <p>that's great!</p> <p>For those who are interested, here is the output :</p> <pre><code> 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)) </code></pre> <p>Yo Eight, if by chance we meet one day, i'll pay you a beer!</p> <p>Benoit</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload