我承认标题不是很明确:对不起。
假设我有一个理解:
for {v1<-Validation1(input)
v2<-Validation2(v1)
v3<-Validation3(v2)
} yield result
Validation1、Validation2 和 Validation3 进行一些检查(例如“年龄 > 18”)并使用失败/成功;所以如果有什么问题,理解中止,我在结果的失败部分得到原因,否则我在成功部分得到预期值。到目前为止,一切都很好,没有什么难的。
但是如果 Validation1、Validation2、Validation3 的输入满足某些规则(例如:“这个人可以投票,因为他的年龄大于 18 岁并且他的国籍是法国”),则它们是成功的。我想要的是跟踪应用的规则,以便能够在最后显示它们。
这显然是一个日志记录的用例。但我在这样做的路上犹豫不决:
拥有一个可被任何函数访问的对象“logger”(Validation1、2 和 3,还有想要显示日志内容的调用者)
使记录器成为Validation1、2和3的参数
等待“Scala 中的函数式编程”的相关章节:)
其他?
感谢您的建议
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
现在,如果平方根成功,我想记录字符串successsquaretoot,如果逆成功,我想记录字符串successinverse,以便函数resultat在成功的情况下累积两个字符串
我按照 Yo Eight 的建议从 ValidationT 开始:
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)))
但我找不到如何将它们组合起来以供理解。此外,要获得其中一个的结果,我必须写: squareroot2(4).run.run 这看起来很奇怪,而且我写它的方式,即使在失败的情况下,字符串 successsquareroot 也会被记录:
println(squareroot2(-1).run.run)
打印:(平方根确定,失败(不能取负数的平方根))
谢谢!贝努瓦
4月12日编辑
所以 Yo 八建议了这个片段:
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
他警告我一些类型注释是必要的。实际上, squareroot 和 inverse 的返回 tpye 相当难看:这是我难以理解的东西的 ValidationT!
因此,我必须明确指定返回类型: def inverse(x:Double) : ValidationT[?,E,A] 其中“E”是字符串,“A”是 Double(这很简单!)。但是第一个呢?它必须是一个单子(据我所知),我选择了最简单的:Id(即身份)。
所以现在我们有:
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)
但是 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)
这使 :
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)
凉爽的!我最好为 Writer 使用 List[String] ,但我认为我做得很好!
现在,我可以考虑我的假期(明天!):)
5月14日编辑
好吧,代码没有编译,但错误在八八的最后一个建议中(注意,这又不是冒犯八八谁是善良的典范!)。我向您提交完整的代码和错误:
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)
}
}
这是终端的会话:
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
我想我从一开始就错过了一些东西,这可以解释为什么我坚持了几个星期!
贝努瓦
5月15日编辑
编译你的代码,我有第一个错误:
could not find implicit value for parameter F: scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias]
经过一些尝试,我以这种方式重写了导入:
import scalaz.Writer
import scalaz.std.string._
import scalaz.Id._
import scalaz.WriterT
import scalaz.ValidationT
import scala.Math._
还有一个错误:
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
您在 5 月 14 日编写的代码中出现了此错误。显然,很难理解使用 scalaz-7 究竟要导入什么内容。使用版本 6,事情看起来更简单:只需导入 scalaz._ 和 Scalaz._
我觉得自己像一个“绝望的家庭主妇”:)(是的,我同意,这不是很精明,但很放松!)
贝努瓦
5月23日
哎呀!它可以有效地与 scalaz-7 的最新版本一起使用:请注意,我必须构建它而不是下载快照。
那太棒了!
对于那些感兴趣的人,这里是输出:
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))
哟八,如果有一天我们偶然相遇,我会给你一杯啤酒!
贝努瓦