Scalaz:在for-understanding和logging中验证

bhe*_*her 9 validation logging scalaz

我承认标题不是很明确:抱歉.

假设我有一个理解:

for {v1<-Validation1(input)
     v2<-Validation2(v1)
     v3<-Validation3(v2)
} yield result
Run Code Online (Sandbox Code Playgroud)

验证1,验证2和验证3进行一些检查(例如"年龄> 18")并使用失败/成功; 所以,如果出现问题,for-comprehension就会中止,我会在结果的失败部分找到原因,否则我会在成功部分获得预期的价值.到目前为止,这么好,也没什么难的.

但Validation1,Validation2,Validation3是成功的,如果他们的输入满足一些规则(例如:"那个人可以投票,因为他的年龄大于18岁,他的国籍是法国人").我想要的是跟踪应用的规则,以便能够在最后显示它们.

这显然是日志记录的一个用例.但我在做这件事的路上犹豫不决:

  1. 拥有一个可由任何函数访问的对象"logger"(Validation1,2和3,以及想要显示日志内容的调用者)

  2. 使记录器成为Validation1,2和3的参数

  3. 等待"Scala中的函数编程"相关章节:)

  4. 其他?

谢谢你的建议

编辑于4月10日

所以,假设我想计算函数:x - > 1/sqrt(x)

首先,我通过检查x> 0来计算sqrt(x)然后如果不是零则采用逆.

使用scalaz.Validation,很简单:

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
Run Code Online (Sandbox Code Playgroud)

现在,如果squareroot成功,我想记录字符串successsquaretoot,如果反向sucesses,我想记录字符串successinverse,以便函数resultat积累两个字符串,以防成功

我开始使用ValidationT,因为Yo Eight建议:

 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)))  
Run Code Online (Sandbox Code Playgroud)

但我无法找到如何将它们结合起来进行理解.此外,为了获得其中一个的结果,我必须写:squareroot2(4).run.run这看起来很奇怪,就像我写的那样,即使在失败的情况下,字符串successsquareroot也会被记录:

 println(squareroot2(-1).run.run)
Run Code Online (Sandbox Code Playgroud)

打印:( Squareroot ok,失败(不能取负数的平方根))

谢谢!伯努瓦

编辑于4月12日

所以Yo Eight建议这个片段:

 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
Run Code Online (Sandbox Code Playgroud)

他警告我,某些类型的注释是必要的.有效的是,squareroot和inverse的返回是相当丑陋的:它是一个我很难理解的东西的验证!

因此,我必须明确指定返回类型:def inverse(x:Double):ValidationT [?,E,A]其中"E"是String而"A"是Double(很简单!).但是第一个怎么样?它必须是monad(据我所知)并且我选择了最简单的:Id(即Identity).

所以现在我们有:

   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)     
Run Code Online (Sandbox Code Playgroud)

但是for-comprehension不能编译,因为"y"不是Double而是WriterT [Id,String,Double]此外,第一个记录的消息("Squareroot ok")是"丢失".

最终,我确实喜欢这样:

   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)
Run Code Online (Sandbox Code Playgroud)

这使 :

  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)
Run Code Online (Sandbox Code Playgroud)

凉!我最好为Writer使用List [String],但我认为我的方法很好!

而现在,我可以想到我的假期(明天!):)

5月14日编辑

好吧,代码没有编译,但错误是在Yo Eight的最后一个建议中(请注意,重要的是,并不是一个冒犯的Yo Eight,它是善良的典范!).我提交了完整的代码和错误:

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)
  }



}
Run Code Online (Sandbox Code Playgroud)

这是终端的会话:

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 => B)Main.MyId[B] is not defined
implicit val myIdPointed = new Pointed[MyId]{
                           ^
    one error found
Run Code Online (Sandbox Code Playgroud)

我想有一些我从一开始就错过的东西可以解释为什么我坚持了几个星期!

伯努瓦

5月15日编辑

编译代码,我有第一个错误:

 could not find implicit value for parameter F:  scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias]
Run Code Online (Sandbox Code Playgroud)

经过一些尝试,我以这种方式重写了导入:

import scalaz.Writer
import scalaz.std.string._
import scalaz.Id._
import scalaz.WriterT
import scalaz.ValidationT
import scala.Math._
Run Code Online (Sandbox Code Playgroud)

还有一个错误:

 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
Run Code Online (Sandbox Code Playgroud)

这个错误出现在你写的代码可能14上.显然,很难理解用scalaz -7完全改变的内容.使用版本6,事情看起来更简单:只需要​​导入scalaz._和Scalaz._

我觉得自己像个"绝望的写作者":)(是的,我同意,这不是很精明,但它很放松!)

伯努瓦

5月23日

OUF!它有效地适用于最新版本的scalaz-seven:请注意,我必须构建它而不是下载快照.

那很棒!

对于那些感兴趣的人,这是输出:

 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))
Run Code Online (Sandbox Code Playgroud)

哟八,如果偶然我们见面一天,我会给你一杯啤酒!

伯努瓦

Yo *_*ght 7

为了在monadic计算期间进行记录,您必须使用Writer monad的实例.由于monad不构成并且您想要保持"验证"效果,因此您应该使用Validation Monad Transformer.我不知道您使用的是哪个版本的ScalaZ,但Scalaz7(分支scalaz-seven)提供了这样的monad转换器(即ValidationT).

所以我们得到:

ValidationT[({type f[x] = Writer[W, x]})#f, A]
Run Code Online (Sandbox Code Playgroud)

用W记录器的类型

根据你的编辑,这是我将如何做到这一点

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)
Run Code Online (Sandbox Code Playgroud)

现在,如何在for-comprehension中使用它

for {
  y <- squareroot(x).flatMapF(i => Writer("Squareroot ok", i))
  z <- inverse(y).flatMapF(i => Writer("Inverse ok", i))
} yield z
Run Code Online (Sandbox Code Playgroud)

这些片段可能需要更多类型注释

编辑于4月13日

这是您的方法的正确类型注释:

 def squareroot(x:Double):ValidationT[({type f[x] = Writer[String, x]})#f,String,Double]
 def inverse(x:Double):ValidationT[{type f[x] = Writer[String, x]})#f,String,Double]
Run Code Online (Sandbox Code Playgroud)

这样,您可以像这样定义resultat方法:

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
Run Code Online (Sandbox Code Playgroud)

您还可以使用List [String]作为日志类型,因为它是一个幺半群

顺便说一句,如果可以提供帮助,我会说法语:-)

编辑于5月14日

问题是:编译器无法解析

implicitly[Pointed[({ type f[x] = Writer[String, x] })#f]]
Run Code Online (Sandbox Code Playgroud)

因为WriterT需要一个Monoid [String]和Pointed [Id]的实例.

import std.string._ // this import all string functions and instances
import Id._         // this import all Id functions and instances
Run Code Online (Sandbox Code Playgroud)

这是完整的可执行代码

import scalaz._
import std.string._
import Id._
import scalaz.WriterT
import scalaz.ValidationT
import scala.Math._

object ValidationTExample extends Application {
  type ValidationTWriterAlias[W, A] = ValidationT[({type f[x] = Writer[W, x]})#f, W, A]
  type WriterAlias[A] = Writer[String, A]

  def squareroot(x:Double): ValidationTWriterAlias[String, Double] = 
    if (x < 0) ValidationT.failureT[WriterAlias, String, Double]("Can't take squareroot of negative number") 
    else ValidationT.successT[WriterAlias, String, Double](sqrt(x))

  def inverse(x:Double): ValidationTWriterAlias[String, Double] = 
    if (x == 0) ValidationT.failureT[WriterAlias, String, Double]("Can't take inverse of zero ") 
    else ValidationT.successT[WriterAlias, 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

  println("0 : " + resultat(0.0).run.run)
  println("-1 : " + resultat(-1.0).run.run)
  println("4 : " + resultat(4).run.run)
}
Run Code Online (Sandbox Code Playgroud)

编辑于8月14日

此代码在scalaz-7中不再有效.ValidationT已被删除,因为验证不是monad.希望可以使用EitherT.此外,还添加了一个新的MonadWriter/ListenableMonadWriter类型类来减轻这些类型注释.

import scalaz._
import std.string._
import syntax.monadwriter._
import scala.Math._

object EitherTExample extends Application {
  implicit val monadWriter = EitherT.monadWriter[Writer, String, String]

  def squareroot(x: Double) =
    if (x < 0)
      monadWriter.left[Double]("Can't take squareroot of negative number")
    else
      monadWriter.right[Double](sqrt(x))

  def inverse(x: Double) = 
    if (x == 0)
      monadWriter.left[Double]("Can't take inverse of zero")
    else
      monadWriter.right[Double](1 / x)

  def resultat(x: Double) = for {
    y <- squareroot(x) :++> "Squareroot ok"
    z <- inverse(y)    :++> ", Inverse ok"
  } yield z

  println("0 : " + resultat(0.0).run.run)
  println("-1 : " + resultat(-1.0).run.run)
  println("4 : " + resultat(4).run.run)
}
Run Code Online (Sandbox Code Playgroud)