Last active
September 21, 2017 15:14
-
-
Save nicocavallo/5d6c6d47b1e40e1bb9d0c189c05734c5 to your computer and use it in GitHub Desktop.
Minimalistic example of CATS Validated / ValidatedNel and Xor / XorT
This file contains hidden or 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.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)))) |
This file contains hidden or 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.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