Skip to content

Instantly share code, notes, and snippets.

@fancellu
Created October 13, 2019 16:51
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/13723b025629bc28247aa3d006c403ca to your computer and use it in GitHub Desktop.
Save fancellu/13723b025629bc28247aa3d006c403ca to your computer and use it in GitHub Desktop.
Example usage of the Cats Ior data type
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)
}
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