This library is a refinement of Cats' Validated for representing the result of parsing some input, to add more context about where any failures occurred.
- Using
Either
in Scala is a way to parse complex data structures and report errors. However, the first failure stops the process, and users need to fix it to see what else is wrong. Thus, failures have to be fixed one by one. - Cats'
Validated
can parse multiple parts of a structure in parallel, reporting all the errors at once. However, when building a complex structure (for example, usingmapN
), the errors do not point to the exact part that failed. - This library offers a
ValidatedC
data type that keeps track of context - the logical place in the structure that is currently being parsed. This context is added to errors so it's easy to pinpoint the problem.
Library | Data parsed | Errors reported | Error context |
---|---|---|---|
Either with a for comprehension |
✅ Arbitrary | ❌ None | |
Validated with mapN |
✅ Arbitrary | ✅ All | ❌ None |
Circe | ✅ JSON path | ||
kantan.csv | |||
scala-parser-combinators, FastParse | ✅ Position | ||
Refinery | ✅ Arbitrary | ✅ All | ✅ User defined |
Let's try to parse a configuration into a simple case class:
import cats.implicits._
case class Box(width: Int, height: Int, depth: Int)
val config: Map[String, String] = Map(
"width" -> "10",
"height" -> "oops",
)
The configuration is invalid (height is not an integer, and depth is missing). How will the errors be reported?
def parseInt(value: String): Either[String, Int] =
value.toIntOption.toRight(s"Invalid integer: $value")
def getConfig(key: String): Either[String, String] =
config.get(key).toRight(s"No key: $key")
println(
(
getConfig("width").flatMap(parseInt),
getConfig("height").flatMap(parseInt),
getConfig("depth").flatMap(parseInt),
).mapN(Box.apply)
)
// => Left(Invalid integer: oops)
Only the first error is reported.
def parseInt(value: String): ValidatedNec[String, Int] =
value.toIntOption.toValidNec(s"Invalid integer: $value")
def getConfig(key: String): ValidatedNec[String, String] =
config.get(key).toValidNec(s"No key: $key")
println(
(
getConfig("width