Я признаю, что заголовок не очень четкий: извините за это.
Предположим, у меня есть понимание:
for {v1<-Validation1(input)
v2<-Validation2(v1)
v3<-Validation3(v2)
} yield result
Validation1, Validation2 и Validation3 выполняют некоторые проверки (например, «возраст > 18») и используют fail/success; поэтому, если что-то не так, понимание прерывается, и я получаю причину в неудачной части результата, иначе я получаю ожидаемое значение в успешной части. Пока все хорошо и ничего сложного.
Но Validation1, Validation2, Validation3 проходят успешно, если их входные данные удовлетворяют некоторым правилам (например: «парень может голосовать, потому что его возраст больше 18 лет и он является французом»). я хочу отслеживать применяемые правила, чтобы иметь возможность отображать их в конце.
Очевидно, что это вариант использования ведения журнала.но я сомневаюсь, как это сделать:
Имейте объект «логгер», доступный любой функции (проверка 1, 2 и 3, а также вызывающая сторона, которая хочет отобразить содержимое журнала)
Сделайте logger параметр Validation1, 2 и 3
Дождитесь соответствующей главы "Функциональное программирование в Scala" :)
Другое?
Спасибо за советы
Отредактировано 10 апреля
Итак, предположим, я хочу вычислить функцию: x -> 1/sqrt(x)
Сначала я вычисляю sqrt(x), проверяя, что x > 0, и тогда я беру обратное, если не ноль.
с 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 накапливала обе строки в случай успеха
Я начал с 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)))
Но я не могу найти, как объединить их в for-comprehension. Кроме того, чтобы получить результат одного из них, я должен написать: квадратный корень2(4).run.run что кажется странным и в том виде, в котором я это написал, даже в случае неудачи в журнал записываются строки SuccessSquareRoot:
println(squareroot2(-1).run.run)
печатает: (Squareroot ok,Failure(Невозможно взять квадратный корень из отрицательного числа))
Спасибо! Бенуа
Отредактировано 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» — это String, а «A» — это Double (это было легко!). Но как насчет первого? Это должна быть монада (насколько я понимаю), и я выбрал самое простое: 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)
Но 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)
Круто! Я бы лучше использовал 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
Думаю, есть что-то, что я пропустил с самого начала, что может объяснить, почему я застрял на несколько недель!
Бенуа
Отредактировано 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._
Чувствую себя "отчаянным домработником" :) (да, согласен, не очень умно, но расслабляет!)
Бенуа
23 мая
Уф! Он эффективно работает с последней версией 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))
Эй Восьмой, если мы случайно встретимся, я заплачу тебе пивом!
Бенуа