Cats Support
This module provides typeclass instances for Cats. Furthermore, it contains refinement methods using Cats' Validated, ValidatedNec and ValidatedNel.
Dependency
SBT:
libraryDependencies += "io.github.iltotore" %% "iron-cats" % "version"
Mill:
ivy"io.github.iltotore::iron-cats:version"
Following examples' dependencies
SBT:
libraryDependencies += "org.typelevel" %% "cats-core" % "2.8.0"
Mill:
ivy"org.typelevel::cats-core::2.8.0"
Accumulative error handling
Cats enables accumulative error handling via Validated. Iron provides refinement methods that return an Either, EitherNec or EitherNel to easily combine runtime refinements with failure accumulation. There are also variants that return a Validated, ValidatedNec or ValidatedNel.
These methods are similar to refineEither and refineOption defined in the core module.
The User example now looks like this:
import cats.data.EitherNec
import cats.syntax.all.*
import io.github.iltotore.iron.*
import io.github.iltotore.iron.cats.*
import io.github.iltotore.iron.constraint.all.*
case class User(name: String :| Alphanumeric, age: Int :| Positive)
def createUserAcc(name: String, age: Int): EitherNec[String, User] =
(
name.refineNec[Alphanumeric],
age.refineNec[Positive]
).parMapN(User.apply)
createUserAcc("Iltotore", 18) //Right(User(Iltotore,18))
createUserAcc("Il_totore", 18) //Left(Chain(Should be alphanumeric))
createUserAcc("Il_totore", -18) //Left(Chain(Should be alphanumeric, Should be greater than 0))
Or with custom messages:
import cats.data.EitherNec
import cats.syntax.all.*
import io.github.iltotore.iron.*
import io.github.iltotore.iron.cats.*
import io.github.iltotore.iron.constraint.all.*
type Username = DescribedAs[Alphanumeric, "Username should be alphanumeric"]
type Age = DescribedAs[Positive, "Age should be positive"]
case class User(name: String :| Username, age: Int :| Age)
def createUserAcc(name: String, age: Int): EitherNec[String, User] =
(
name.refineNec[Username],
age.refineNec[Age]
).parMapN(User.apply)
createUserAcc("Iltotore", 18) //Right(User(Iltotore,18))
createUserAcc("Il_totore", 18) //Left(Chain(Username should be alphanumeric))
createUserAcc("Il_totore", -18) //Left(Chain(Username should be alphanumeric, Age should be positive))
Leveraging typeclass instances via Cats' syntax.
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*
import io.github.iltotore.iron.cats.given
val name1: String :| Alphanumeric = "Martin".refineUnsafe
val name2: String :| Alphanumeric = "George"
val age1: Int :| Greater[0] = 60
name1.show // Martin
name1 |+| name2 // MartinGeorge
age1 === 49 // false
Companion object (RefinedTypeOps extensions)
Companion object created with RefinedTypeOps is being extended by set of functions.
Companion object
import io.github.iltotore.iron.*
import io.github.iltotore.iron.constraint.all.*
opaque type Temperature = Double :| Positive
object Temperature extends RefinedTypeOps[Temperature]
Imports
import io.github.iltotore.iron.cats.*
functions
All the example return cats structures with either result or error report.
Temperature.eitherNec(-5.0)Temperature.eitherNel(-5.0)Temperature.validated(-5.0)Temperature.validatedNec(-5.0)Temperature.validatedNel(-5.0)