Я допускаю, что название не очень явное: извините за это.
Предположим, у меня есть для понимания:
for {v1<-Validation1(input)
v2<-Validation2(v1)
v3<-Validation3(v2)
} yield result
Валидация 1, Валидация 2 и Валидация 3 выполняют некоторую проверку (например, «возраст> 18») и используют сбой / успех; поэтому, если что-то не так, из-за невозможности понимания я получаю причину в части неудачи результата, иначе я получаю ожидаемое значение в части успеха. Пока все хорошо и ничего сложного.
Но Валидация 1, Валидация 2, Валидация 3 успешны, если их вклад удовлетворяет некоторым правилам (например, «парень может голосовать, потому что его возраст больше 18 лет, а его гражданство - француз»). я хочу отслеживать, какие правила применяются, чтобы их можно было отображать в конце.
Это явно случай использования логирования. но я не решаюсь сделать это:
Имейте объект "регистратор", который доступен любой функции (Validation1, 2 и 3, но также вызывающему, который хочет отобразить содержание журнала)
Сделать регистратор параметром Validation1, 2 и 3
Дождитесь соответствующей главы «Функциональное программирование в Scala»:)
Другое
Спасибо за советы
Отредактировано 10 апреля
Итак, предположим, что я хочу вычислить функцию: x -> 1 / sqrt (x)
Сначала я вычисляю sqrt (x), проверяя, что x> 0, а затем беру обратное, если не ноль.
со скалязом. Валидация, это просто:
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 накапливала обе строки в случае успеха
Я начал с 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)))
Но я не могу найти, как объединить их для понимания.
Кроме того, чтобы получить результат одного из них, мне нужно написать:
squareroot2 (4) .run.run
что странно
и так, как я написал, даже в случае сбоя записывается строка successsquareroot:
println(squareroot2(-1).run.run)
печатает: (квадратный корень в порядке, ошибка (невозможно получить квадратный корень отрицательного числа))
Спасибо!
Benoit
Отредактировано 12 апреля
Итак, Йо Восьми предложил этот фрагмент:
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
и он предупредил меня, что нужны аннотации некоторых типов. В действительности, возвращаемое значение квадратного корня и обратное довольно уродливо: это проверка того, что мне было трудно понять!
Итак, я должен был явно указать тип возвращаемого значения: def inverse (x: Double): ValidationT [?, E, A] где «E» - это строка, а «A» - двойная (это было легко!). Но как насчет первого? Это должна быть монада (насколько я понимаю), и я выбрал самое простое: 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)
Но для понимания не компилируется, потому что "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)
Cool! Я бы лучше использовал List [String] для Writer, но я думаю, что я на правильном пути!
А теперь я могу подумать о моих выходных (завтра!):)
Отредактировано 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)
}
}
Вот сеанс терминала:
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
Полагаю, что с самого начала я что-то упустил, и это может объяснить, почему я застрял на несколько недель!
Benoit
Отредактировано 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
Эта ошибка присутствовала в коде, который вы написали 14 мая. Очевидно, что сложно понять, что именно импортировать с помощью scalaz-seven. С версией 6 все выглядело проще: нужно было просто импортировать scalaz._ и Scalaz._
Я чувствую себя "отчаянным домохозяином" :) (да, я согласен, это не очень проницательно, но это расслабляет!)
Benoit
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))
Эй, восемь, если случайно мы встретимся однажды, я заплачу тебе пиво!
Benoit