次のシグネチャを持つメソッドを書きたいとします。
def parse(input: List[(String, String)]):
ValidationNel[Throwable, List[(Int, Int)]]
入力内の文字列のペアごとに、両方のメンバーが整数として解析できること、および最初のメンバーが 2 番目のメンバーよりも小さいことを確認する必要があります。次に、発生したエラーを累積して整数を返す必要があります。
まず、エラー タイプを定義します。
import scalaz._, Scalaz._
case class InvalidSizes(x: Int, y: Int) extends Exception(
s"Error: $x is not smaller than $y!"
)
これで、次のようにメソッドを実装できます。
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))
)
または、代わりに:
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
)
なんらかの理由で、最初の操作 (ペアが文字列として解析されることの検証)は検証の問題のように感じますが、2 番目の操作 (値のチェック)は論理和の問題のように感じます。これは、モナドインスタンスがないため、を使用する必要があることを示唆しています)。\/
ValidationNel[Throwable, _]
私の最初の実装では、ValidationNel
全体を通して使用fold
し、最後に一種の偽物として使用しflatMap
ます。2 つ目では、エラーの蓄積が必要か、モナド バインドが必要かによって、 と の間を適切にValidationNel
行き来します。\/
それらは同じ結果を生成します。
私は実際のコードで両方のアプローチを使用してきましたが、まだどちらかを優先していません。何か不足していますか?どちらかを優先する必要がありますか?