Skip to content

Instantly share code, notes, and snippets.

@fancellu
Last active February 25, 2021 13:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fancellu/b6deaed2068ff5bd91f544d178568560 to your computer and use it in GitHub Desktop.
Save fancellu/b6deaed2068ff5bd91f544d178568560 to your computer and use it in GitHub Desktop.
Example usage of the Cats Validated data type
NonEmptyList(failed1, failed2)
1223
Invalid(NonEmptyList(name must not be none, age must not be none))
Invalid(NonEmptyList(name must not be none, age must not be none, town must not be empty))
Invalid(NonEmptyList(name must not be none, age must be adult >=18))
Valid(Person(Dave,23,Some(Epsom),None))
import cats._
import cats.data._
import cats.implicits._
import cats.data.{NonEmptyList => NEL}
// Example usage of the Cats Validated data type. It does not form a Monad (no flatMap)
// unlike Either, so does not stop on first failure
object ValidatedExample extends App {
type ERRORS = NEL[String]
// mapN will add up all the ints if they are ALL valid.
// otherwise will return ALL the invalid strings in a NonEmptyList
// By definition, if we fail, the failure list cannot be empty, hence NEL
val failed: Validated[ERRORS, Int] = (100.valid[ERRORS], 123.valid[ERRORS],
NEL.of("failed1").invalid[Int], NEL.of("failed2").invalid[Int]).mapN(_ + _ + _ + _)
// we process the return value with fold.
// map only expressed for Valid, NOP otherwise
failed.map(_ + 1000).fold(
errors => println(errors),
intsum => println(intsum)
)
val worked: Validated[ERRORS, Int] = (100.valid[ERRORS], 123.valid[ERRORS]).mapN(_ + _)
worked.map(_ + 1000).fold(
errors => println(errors),
intsum => println(intsum)
)
// A more real world example
case class PersonInput(name: Option[String], age: Option[Int], town: Option[String], misc: Option[String])
case class Person(name: String, age: Int, town: Option[String], misc: Option[String])
def parsePersonInput(personInput: PersonInput): Validated[ERRORS, Person] =
(
Validated.fromOption(personInput.name, NEL.of("name must not be none")).
ensure(NEL.of("name must not be empty"))(!_.isEmpty),
Validated.fromOption(personInput.age, NEL.of("age must not be none")).
ensure(NEL.of("age must be adult >=18"))(_ >= 18),
// town can be none, but if present must not be an empty string
personInput.town.valid.ensure(NEL.of("town must not be empty"))(!_.exists(_.isEmpty)),
// we don't care about misc, so always seen as valid
personInput.misc.valid
).mapN(Person)
val err0 = parsePersonInput(PersonInput(None, None, None, None))
println(err0)
val err1 = parsePersonInput(PersonInput(None, None, "".some, None))
println(err1)
val err2 = parsePersonInput(PersonInput(None, 17.some, "Epsom".some, "blah".some))
println(err2)
val ok = parsePersonInput(PersonInput("Dave".some, 23.some, "Epsom".some, None))
println(ok)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment