Validação versus disjunção

Suponha que eu queira escrever um método com a seguinte assinatura:

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

Para cada par de strings na entrada, é necessário verificar se ambos os membros podem ser analisados ​​como inteiros e se o primeiro é menor que o segundo. Em seguida, ele precisa retornar os números inteiros, acumulando todos os erros que aparecerem.

Primeiro eu vou definir um tipo de erro:

import scalaz._, Scalaz._

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

Agora posso implementar meu método da seguinte maneira:

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

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

Agora, por qualquer motivo, a primeira operação (validando que os pares analisam como strings)sente para mim como um problema de validação, enquanto o segundo (verificando os valores)sente como um problema de disjunção, esente como eu preciso para compor os dois monadicamente (o que sugere que eu deveria estar usando\/, Desde aValidationNel[Throwable, _] não tem uma instância monad).

Na minha primeira implementação, eu usoValidationNel ao longo e depoisfold no final, como uma espécie de falsoflatMap. No segundo, eu volto e para trás entreValidationNel e\/ conforme apropriado, dependendo se eu precisar de acumulação de erro ou ligação monádica. Eles produzem os mesmos resultados.

Eu usei ambas as abordagens em código real e ainda não desenvolvi uma preferência por uma sobre a outra. Estou esquecendo de algo?Devemos Eu prefiro um sobre o outro?

questionAnswers(1)

yourAnswerToTheQuestion