Skip to content

Instantly share code, notes, and snippets.

@nicocavallo
Last active September 21, 2017 15:14
Show Gist options
  • Save nicocavallo/5d6c6d47b1e40e1bb9d0c189c05734c5 to your computer and use it in GitHub Desktop.
Save nicocavallo/5d6c6d47b1e40e1bb9d0c189c05734c5 to your computer and use it in GitHub Desktop.
Minimalistic example of CATS Validated / ValidatedNel and Xor / XorT
import cats._
import cats.data.Validated.{Invalid, Valid}
import cats.data.{ValidatedNel, _}
import cats.implicits._
case class Person(name: String, jobTitle: String, age: Int)
case class Error(msg: String)
type ValidationResult[T] = ValidatedNel[Error,T]
def validateName(personName: String): ValidationResult[String] =
if (Option(personName).isDefined && personName.length > 0 )
Valid(personName)
else
Invalid(Error(s"The name must not be empty")).toValidatedNel
def validateJobTitle(jobTitle: String): ValidationResult[String] =
if (Option(jobTitle).isDefined && jobTitle.toLowerCase.contains("developer"))
Valid(jobTitle)
else
Invalid(Error("Sorry, we only want people with experience in software development")).toValidatedNel
def validateAge(age: Int): ValidationResult[Int] =
if (age > 3)
Valid(age)
else
Invalid(Error("We can't hire toddlers")).toValidatedNel
val candidate1 =
(
validateName("Bill Gates") |@| validateJobTitle("VB Developer") |@| validateAge(25)
) map Person.apply
// candidate1: ValidationResult[Person] = Valid(Person(Bill Gates,VB Developer,21))
val candidate2 =
(
validateName("Nicolas Cavallo") |@| validateJobTitle("Junior Programmer") |@| validateAge(73)
) map Person.apply
// candidate2: ValidationResult[Person] =
// Invalid(OneAnd(Error(Sorry, we only want people with experience in software development),List()))
val candidate3 =
(
validateName("") |@| validateJobTitle("Project Manager") |@| validateAge(2)
) map Person.apply
// candidate3: ValidationResult[Person] =
// Invalid(OneAnd(Error(The name must not be empty),List(Error(Sorry, we only want people with experience in software development), Error(We can't hire toddlers))))
import cats.data.{Xor, XorT}
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
case class User(name: String)
trait UserFinder {
def findUser(name: String): Future[Option[User]] = name match {
case "foo" => Future successful None
case _ => Future successful Some(User(name))
}
}
trait UserUpdater {
def updateUser(user: User): Future[Boolean] = user match {
case User("faulty") => Future successful false
case _ => Future successful true
}
}
trait ImageUpdater {
def updateImages(images: List[String]): Future[Boolean] =
Future successful (images.length <= 10)
}
object Program extends UserFinder with UserUpdater with ImageUpdater {
case class Error(msg: String)
case class UpdatedUser(user: User, images: List[String])
type Result[T] = Future[Either[Error,T]]
def toXor(errorMessage: String)(v: Boolean): Xor[Error,Boolean] =
if (v) Xor.right[Error, Boolean](v)
else Xor.left[Error, Boolean](Error(errorMessage))
def updateAll(name: String, images: List[String]): Result[UpdatedUser] = {
(for {
user <- XorT(findUser(name).map(_.toRightXor(Error("User not found"))))
updated <- XorT(updateUser(user).map(toXor("Error persisting user")))
withImages <- XorT(updateImages(images).map(toXor("Error persisting images")))
} yield {
UpdatedUser(user, images)
}) toEither
}
}
import Program.updateAll
val magicN = 11
Await.result(updateAll("Nico", List.empty), 1 second)
// res0: Either[Program.Error,Program.UpdatedUser] = Right(UpdatedUser(User(Nico),List()))
Await.result(updateAll("foo", List.fill(magicN)("picture.png")), 1 second)
// res1: Either[Program.Error,Program.UpdatedUser] = Left(Error(User not found))
Await.result(updateAll("faulty", List.fill(magicN)("picture.png")), 1 second)
// res2: Either[Program.Error,Program.UpdatedUser] = Left(Error(Error persisting user))
Await.result(updateAll("Maby", List.fill(magicN)("picture.png")), 1 second)
// res3: Either[Program.Error,Program.UpdatedUser] = Left(Error(Error persisting images))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment