Validation
A standalone Validation data type, called Result
. (see How do I error handle thee? for more on the general idea of a Validation)
The Result
type is very similar to scalaz.Validation
, cats.data.Validated
, or org.scalactic.Or
with the following differences:
- no dependencies besides scala and no additional baggage
that is no Monads, Applicative Functors, Arrows, or Categories. The only thing that is provided besides Result
is a NonEmptyVector
which is similar to a regular Vector
, only that there will always be at least one element in it.
The main incentive is, that Result
can be used in places where a full-fledged dependency on scalaz
and co. is undesired, e.g. in projects with many people that are unfamiliar with or intimidated by scalaz
.
- explicit symbolic and unsafe operators
By default, all operations are safe, meaning they are referentially transparent if their input is. Unsafe operations (foreach
, get
) can be enabled by importing validation.Result.unsafe._
All methods are named aptly (or so I hope) and with ascii characters. Symbolic names (such as |
for getOrElse
or |@|
for and
) can be enabled by importing validation.Result.symbolic._
- implied underlying Vector to accumulate data
Scalaz differentiates between Validation[E, A]
and type ValidationNel[E, A] = Validation[NonEmptyList[E], A]
where as Result[E, A]
has an implied NonEmptyVector
accumulating the E
. It behaves mostly as a permanent (imaginary) ResultNev[E, A]
.
What Validation is not
This library is not an implementation of a validation framework, just a general data structure.
Installing
// scala 2.11.x
libraryDependencies += "de.knutwalker" %% "validation" % "0.2.0"
// scala 2.12.x
libraryDependencies += "de.knutwalker" %% "validation" % "0.3.0"
Usage
import validation._
type Validated[A] = Result[String, A]
case class Person(name: String, age: Int)
def parseName(s: String): Validated[String] =
if (s.trim.nonEmpty) Result.valid(s.trim)
else Result.invalid(s"invalid name: '$s'")
def parseAge(s: String): Validated[Int] =
Result.catching[NumberFormatException]
.using(_.getMessage)
.run(s.trim.toInt)
.filter(_ >= 0, s"invalid age: '$s'")
def parsePerson(name: String, age: String): Validated[Person] =
(parseName(name) and parseAge(age)) apply Person
parsePerson("Bernd", "42")
// res0: Validated[Person] = Valid(Person(Bernd,42))
parsePerson("Bernd", "fortytwo")
// res1: Validated[Person] = Invalid(For input string: "fortytwo")
parsePerson("Bernd", "-1337")
// res2: Validated[Person] = Invalid(invalid age: '-1337')
parsePerson("", "")
// res3: Validated[Person] = Invalids(NonEmptyVector(invalid name: '',For input string: ""))