Scalaz: проверка в понимании и ведение журнала

Я признаю, что заголовок не очень четкий: извините за это.

Предположим, у меня есть понимание:

for {v1<-Validation1(input)
     v2<-Validation2(v1)
     v3<-Validation3(v2)
} yield result

Validation1, Validation2 и Validation3 выполняют некоторые проверки (например, «возраст > 18») и используют fail/success; поэтому, если что-то не так, понимание прерывается, и я получаю причину в неудачной части результата, иначе я получаю ожидаемое значение в успешной части. Пока все хорошо и ничего сложного.

Но Validation1, Validation2, Validation3 проходят успешно, если их входные данные удовлетворяют некоторым правилам (например: «парень может голосовать, потому что его возраст больше 18 лет и он является французом»). я хочу отслеживать применяемые правила, чтобы иметь возможность отображать их в конце.

Очевидно, что это вариант использования ведения журнала.но я сомневаюсь, как это сделать:

  1. Имейте объект «логгер», доступный любой функции (проверка 1, 2 и 3, а также вызывающая сторона, которая хочет отобразить содержимое журнала)

  2. Сделайте logger параметр Validation1, 2 и 3

  3. Дождитесь соответствующей главы "Функциональное программирование в Scala" :)

  4. Другое?

Спасибо за советы

Отредактировано 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))

Эй Восьмой, если мы случайно встретимся, я заплачу тебе пивом!

Бенуа

9
задан bhericher 23 May 2012 в 19:47
поделиться