2012-03-27 19 views
9

Przyznaję, że tytuł nie jest zbyt jednoznaczny: przepraszam za to.Scalaz: sprawdzanie poprawności w zrozumieniu i rejestrowaniu

Załóżmy Mam do-zrozumienia:

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

Validation1, Validation2 i Validation3 zrobić kilka kontroli (np "wiek> 18") i wykorzystania fail/sukces; więc jeśli coś jest nie tak, to zrozumienie przerywane i dostaję przyczynę w części porażki wyniku, w przeciwnym razie otrzymam oczekiwaną wartość w części dotyczącej sukcesu. Jak dotąd, tak dobrze i nic bardzo trudnego.

Ale sprawdzanie poprawności1, sprawdzanie poprawności2, sprawdzanie poprawności3 zakończyły się pomyślnie, jeśli ich dane wejściowe spełniają pewne reguły (np. "Facet może głosować, ponieważ jego wiek jest większy niż 18, a narodowość to francuski"). to, czego chcę, to śledzenie reguł, które są stosowane, aby móc je wyświetlić na końcu.

Jest to wyraźnie przypadek użycia logowania. ale waham się na drodze, aby to zrobić:

  1. Czy obiekt „rejestratora”, który jest dostępny przez dowolną funkcję (Validation1, 2 i 3, ale również osoby dzwoniącej, który chce wyświetlać zawartość dziennika)

  2. Wykorzystaj rejestrator parametrem Validation1, 2 i 3

  3. Poczekaj na odnośnym rozdziale "programowanie funkcyjne w Scala" :)

  4. nawzajem?

Dziękuję za rady

Zmieniano 10 kwietnia

Więc załóżmy, że chcę, aby obliczyć funkcję: x -> 1/sqrt (x)

pierwszy, Obliczam sqrt (x), sprawdzając, że x> 0, a następnie przyjmuję odwrotność, jeśli nie jest zero.

z scalaz.Validation, to jest proste:

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 

Teraz, jeśli SquareRoot sukcesy, chcę zalogować się successsquaretoot ciąg i jeśli odwrotnych sucesses, chcę zalogować się successinverse ciąg tak, że funkcja Resultat gromadzi oba ciągi w przypadku powodzenia

zacząłem ValidationT jak Yo Osiem zasugerował:

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

Ale nie mogę znaleźć h łącząc je w celu zrozumienia. Ponadto, aby uzyskać wynik jednego z nich, muszę napisać: squareroot2 (4) .run.uruchomić co wydaje się dziwne iw sposób pisałem, nawet w razie awarii successsquareroot struny jest logowanie:

println(squareroot2(-1).run.run) 

nadrukami: (SquareRoot ok, Failure (Nie można wziąć SquareRoot z liczby ujemnej))

Dziękujemy! Benoit

Edited 12 kwietnia

Więc Yo Osiem zaproponował ten fragment:

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 

i ostrzegł mnie, że niektóre adnotacje typu była konieczna. Efektywnie, powrót trojga pierwiastków kwadratowych i odwrotnych jest raczej brzydki: jest to walidacja czegoś, co trudno mi zrozumieć!

Musiałem więc wyraźnie określić typ zwrotu: def inverse (x: Double): ValidationT [?, E, A], gdzie "E" to String, a "A" Double (to było łatwe!). Ale co z pierwszym? Musi to być monada (o ile rozumiem) i wybrałem prostszą: Id (Tożsamość).

Więc teraz mamy:

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)  

Ale za-pojmowania nie kompiluje bo "y" nie jest podwójny, ale WriterT [Id, String, Double] Ponadto pierwszy zalogowanych wiadomość ("Squareroot ok") jest "zagubiony".

Ostatecznie zrobiłem tak:

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) 

co daje:

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! Byłoby mi lepiej użyć List [String] dla Writera, ale myślę, że jestem na dobrej drodze!

A teraz mogę myśleć do moich wakacji (jutro!) :)

Zmieniano 14 maja

dobrze, kod nie kompiluje, ale błąd jest w Yo Osiem Zeszły sugestia (zauważ, że to nie jest obraza ponownie Yo Eight, który jest wzorem życzliwości!). Przesłać Ci pełny kod i błąd:

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



} 

Oto sesja terminalu:

[email protected]:~$ cd scala 
[email protected]:~/scala$ scala -version 
Scala code runner version 2.9.2 -- Copyright 2002-2011, LAMP/EPFL 
[email protected]:~/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 

Chyba, że ​​jest coś, że tęskniłem od początku, że może wyjaśnić dlaczego jestem przyklejone na kilka tygodni!

Benoit

Edited 15 maja

Kompilacja kodu, mam pierwszy błąd:

could not find implicit value for parameter F: scalaz.Pointed[Main.$anon.ValidationTExample.WriterAlias] 

Po kilku próbach I przepisał import w ten sposób:

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

Istnieje sti ll jeden błąd:

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 

Ten błąd był obecny z kodem, który napisałeś maja 14. Oczywiście, trudno jest zrozumieć, co iimport dokładnie scalaz siedem. Używając wersji 6, wszystko wyglądało prościej: wystarczyło zaimportować scalaz._ i Scalaz._

Czuję się jak "zdesperowany domownik" :) (tak, zgadzam się, to nie jest zbyt przenikliwe, ale jest relaksujące!)

Benoit

23 maja

Ouf! Skutecznie działa z ostatnią wersją scalaz-seven: zauważ, że musiałem ją zbudować zamiast pobierać migawkę.

to świetnie!

Dla tych, którzy są zainteresowani, tutaj jest wyjście:

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

Yo Osiem, jeśli przypadkowo spotykamy jeden dzień, Zapłacę ci piwo!

Benoit

+0

Myślę, że twoja wersja skalaza-siedem nie jest dobra. Kod działa poprawnie z ostatnią wersją scalaz-seven branch –

+0

Tęsknię za Twoim wpisem. Tak właśnie się domyślałem. Używam migawki z 14 kwietnia. Powinienem mieć czas na przetestowanie ostatniej wersji tego wieczoru. – bhericher

Odpowiedz

7

W celu zalogowania podczas monadycznej obliczeń, trzeba użyć instancji Writer monady. Ponieważ monada nie komponuje się i chcesz zachować efekt "Walidacja", powinieneś użyć Walidacyjnego Monad Transformatora. Nie wiem, której wersji ScalaZ używasz, ale Scalaz7 (gałąź scalaz-seven) dostarcza takiego transformatora monad (mianowicie ValidationT).

więc otrzymujemy:

ValidationT[({type f[x] = Writer[W, x]})#f, A] 

z W rodzaju rejestratora

Według edycję, oto jak zrobię to

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) 

A teraz, jak go używać w zrozumieniu?

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

Te fragmenty mogą potrzebować więcej adnotacji typu

redakcją 13 kwietnia

Oto poprawne adnotacje przeznaczona do pracy metodami:

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] 

ten sposób można zdefiniować metodę résultat tak :

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 

You mogą również korzystać z listy [łańcuch] jako rodzaj dziennika, ponieważ jest to monoid

BTW, ja mówię po francusku, jeśli to może pomóc :-)

Edytuj 14 maja

Problem polegał : Kompilator nie może rozwiązać problemu z tym, że WriterT potrzebuje instancji Monoid [String] i Pointed [Id].

import std.string._ // this import all string functions and instances 
import Id._   // this import all Id functions and instances 

Oto pełny kod wykonywalny

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

Edycja 14 sierpnia

Ten kod nie jest już ważna w scalaz siedem. ValidationT został usunięty, ponieważ sprawdzanie poprawności nie jest monadą. Mamy nadzieję, że zamiast tego można użyć EitherT. Poza tym dodano nową czcionkę MonadWriter/ListenableMonadWriter, aby złagodzić adnotacje typu.

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

Dziękuję Yo Eight: Używam Scalaza 6.0.4 i sprawdzę, co zapewnia. W każdym razie rozumiem ten pomysł. – bhericher

+0

Jestem przyklejony! Jeśli zastąpię Validation1, Validation2 i Validation3 przez ValidationT [...] (Writer ("blabla", Validation1 (x)) etc ..., pomyślnie łańcuch 3 sprawdzania poprawności, ale nie widzę, jak gromadzić dzienniki ... – bhericher

+1

Rejestr jest "nagromadzony" niejawnie, gdy wywoływana jest właściwość Writer.flatMap (wymagana jest instancja Monoid [W]) .Funkcja Writer.flatMap jest wywoływana niejawnie po wywołaniu ValidationT.flatMap. –

Powiązane problemy