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)