simbolo logica exclusiva ejemplos disyuncion conjuncion condicional validation scala functional-programming scalaz either

validation - logica - Validación versus disyunción



disyuncion y conjuncion (1)

Supongamos que quiero escribir un método con la siguiente firma:

def parse(input: List[(String, String)]): ValidationNel[Throwable, List[(Int, Int)]]

Para cada par de cadenas en la entrada, es necesario verificar que ambos miembros puedan analizarse como enteros y que el primero sea más pequeño que el segundo. Luego debe devolver los números enteros, acumulando cualquier error que aparezca.

Primero definiré un tipo de error:

import scalaz._, Scalaz._ case class InvalidSizes(x: Int, y: Int) extends Exception( s"Error: $x is not smaller than $y!" )

Ahora puedo implementar mi método de la siguiente manera:

def checkParses(p: (String, String)): ValidationNel[NumberFormatException, (Int, Int)] = p.bitraverse[ ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int ]( _.parseInt.toValidationNel, _.parseInt.toValidationNel ) def checkValues(p: (Int, Int)): Validation[InvalidSizes, (Int, Int)] = if (p._1 >= p._2) InvalidSizes(p._1, p._2).failure else p.success def parse(input: List[(String, String)]): ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p => checkParses(p).fold(_.failure, checkValues _ andThen (_.toValidationNel)) )

O alternativamente:

def checkParses(p: (String, String)): NonEmptyList[NumberFormatException] // (Int, Int) = p.bitraverse[ ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int ]( _.parseInt.toValidationNel, _.parseInt.toValidationNel ).disjunction def checkValues(p: (Int, Int)): InvalidSizes // (Int, Int) = (p._1 >= p._2) either InvalidSizes(p._1, p._2) or p def parse(input: List[(String, String)]): ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p => checkParses(p).flatMap(s => checkValues(s).leftMap(_.wrapNel)).validation )

Ahora, por la razón que sea, la primera operación (validando que los pares se analizan como cadenas) me parece un problema de validación, mientras que la segunda (verificando los valores) se siente como un problema de disyunción, y siento que necesito componer los dos de manera monádica ( lo que sugiere que debería estar usando // , ya que ValidationNel[Throwable, _] no tiene una instancia de mónada).

En mi primera implementación, uso ValidationNel todas partes y luego me fold al final como una especie de flatMap falso. En el segundo, rebote entre ValidationNel y // según sea apropiado, dependiendo de si necesito la acumulación de errores o el enlace monádico. Producen los mismos resultados.

He usado ambos enfoques en código real, y aún no he desarrollado una preferencia por uno sobre el otro. ¿Me estoy perdiendo de algo? ¿ Prefiero uno sobre el otro?


Probablemente esta no sea la respuesta que está buscando, pero me di cuenta de que la Validation tiene los siguientes métodos

/** Run a disjunction function and back to validation again. Alias for `@//` */ def disjunctioned[EE, AA](k: (E // A) => (EE // AA)): Validation[EE, AA] = k(disjunction).validation /** Run a disjunction function and back to validation again. Alias for `disjunctioned` */ def @//[EE, AA](k: (E // A) => (EE // AA)): Validation[EE, AA] = disjunctioned(k)

Cuando los vi, realmente no podía ver su utilidad hasta que recordé esta pregunta. Le permiten hacer un enlace adecuado convirtiéndose a disyunción.

def checkParses(p: (String, String)): ValidationNel[NumberFormatException, (Int, Int)] = p.bitraverse[ ({ type L[x] = ValidationNel[NumberFormatException, x] })#L, Int, Int ]( _.parseInt.toValidationNel, _.parseInt.toValidationNel ) def checkValues(p: (Int, Int)): InvalidSizes // (Int, Int) = (p._1 >= p._2) either InvalidSizes(p._1, p._2) or p def parse(input: List[(String, String)]): ValidationNel[Throwable, List[(Int, Int)]] = input.traverseU(p => checkParses(p).@//(_.flatMap(checkValues(_).leftMap(_.wrapNel))) )