Last active
February 25, 2021 13:48
-
-
Save fancellu/b6deaed2068ff5bd91f544d178568560 to your computer and use it in GitHub Desktop.
Example usage of the Cats Validated data type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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