Created
October 13, 2019 16:51
-
-
Save fancellu/13723b025629bc28247aa3d006c403ca to your computer and use it in GitHub Desktop.
Example usage of the Cats Ior 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
import cats._ | |
import cats.data._ | |
import cats.implicits._ | |
import cats.data.{NonEmptyList => NEL} | |
import cats.data.{NonEmptyChain => NEC} | |
// Example usage of the Cats Ior data type. Similar to Validated/Either/Xor, but we can | |
// return both types should we wish | |
// Note the similarity/difference with Validated | |
// https://gist.github.com/fancellu/b6deaed2068ff5bd91f544d178568560 | |
object IorExample extends App{ | |
type ERRORSORWARNING = NEL[String] | |
// mapN will add up all the ints if they are ALL not Left. | |
// and will return ALL the invalid/warning strings in a NonEmptyList | |
// By definition, if we fail or warn, the list cannot be empty, hence NEL | |
// leftNel and bothNel simply call Nel.of for us | |
// Note, I have used infix version for this type vs Ior[ERRORSORWARNING, Int] | |
val failed: ERRORSORWARNING Ior Int = (100.rightIor, 50.rightIor,Ior.bothNel("warning",10), | |
NEL.of("failed1").leftIor[Int], Ior.leftNel[String, Int]("failed2")).mapN(_ + _ + _ + _ + _) | |
// we process the return value with fold. | |
// map only expressed for right or both, NOP otherwise | |
// Note we fail early on Left, and get no returned value | |
failed.map(_ + 1000).fold( | |
errorswarnings => println(errorswarnings), | |
intsum => (), | |
(errorswarnings,intsum) => () | |
) | |
// this time using NEC instead of NEL | |
// Chain is nice, performant https://typelevel.org/cats/datatypes/chain.html | |
val failedChain: String IorNec Int = (100.rightIor, 50.rightIor,Ior.bothNec("warning",10), | |
NEC.one("failed1").leftIor[Int], Ior.leftNec[String, Int]("failed2")).mapN(_ + _ + _ + _ + _) | |
failedChain.map(_ + 1000).fold( | |
errorswarnings => println(errorswarnings), | |
intsum => (), | |
(errorswarnings,intsum) => () | |
) | |
val worked: Ior[ERRORSORWARNING, Int] = (100.rightIor, 50.rightIor, Ior.bothNel("warning",10),Ior.bothNel("warning2",20)).mapN(_ + _ + _ + _) | |
// here we get warnings and a value. It does not fail early on Both, only Left | |
worked.map(_ + 1000).fold( | |
errors => (), | |
intsum => (), | |
(errorswarnings,intsum) => println((errorswarnings,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]) | |
// We can emit fail early errors via Left, warnings via Both, and values via Right | |
def parsePersonInput(personInput: PersonInput): Ior[ERRORSORWARNING, Person] = ( | |
personInput.name.map(name=>if (name.isEmpty) Ior.leftNel("name must not be empty") else name.rightIor).getOrElse(Ior.leftNel("name must not be none")), | |
personInput.age.map(age => if (age < 18) Ior.leftNel("age must be adult >=18") else age.rightIor).getOrElse(Ior.leftNel("age must not be none")), | |
personInput.town.map(town=>if (town.size<3) Ior.bothNel("town length is a bit short",town.some) else town.some.rightIor).getOrElse(none.rightIor), | |
personInput.misc.rightIor | |
).mapN(Person) | |
val noname = parsePersonInput(PersonInput(None, None, None, None)) | |
println(noname) | |
val emptyname = parsePersonInput(PersonInput("".some, None, "".some, None)) | |
println(emptyname) | |
val townwarning = parsePersonInput(PersonInput("Bill".some, 40.some, "ab".some, None)) | |
println(townwarning) | |
val tooyoung = parsePersonInput(PersonInput("John".some, 17.some, "Epsom".some, "blah".some)) | |
println(tooyoung) | |
val ok = parsePersonInput(PersonInput("Dave".some, 23.some, "Epsom".some, "misc".some)) | |
println(ok) | |
} |
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(warning, failed1) | |
Chain(warning, failed1) | |
(NonEmptyList(warning, warning2),1180) | |
Left(NonEmptyList(name must not be none)) | |
Left(NonEmptyList(name must not be empty)) | |
Both(NonEmptyList(town length is a bit short),Person(Bill,40,Some(ab),None)) | |
Left(NonEmptyList(age must be adult >=18)) | |
Right(Person(Dave,23,Some(Epsom),Some(misc))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment