Skip to content

Latest commit

 

History

History
105 lines (77 loc) · 4.28 KB

custom_predicates.md

File metadata and controls

105 lines (77 loc) · 4.28 KB

Custom predicates

The library comes with a lot predefined predicates but also allows to define your own. This example shows how to add predicates for a simple type representing a point in a two-dimensional Cartesian coordinate system. We start by defining a Point class that represents a point in our coordinate system:

scala> case class Point(x: Int, y: Int)
defined class Point

The axes of a two-dimensional Cartesian coordinate system divide the plane into four infinite regions, called quadrants, which are often numbered 1st to 4th. Suppose we want to refine Points with the quadrant they are lying in. So let's create simple types that represent the four quadrants:

scala> case class Quadrant1()
defined class Quadrant1

scala> case class Quadrant2()
defined class Quadrant2

scala> case class Quadrant3()
defined class Quadrant3

scala> case class Quadrant4()
defined class Quadrant4

We now have type-level predicates and a type that we want to refine with these predicates. The next step is to define instances of the Validate type class for Point that are indexed by the corresponding quadrant predicate. We use the Validate.fromPredicate function to create the instances from two functions, one that checks if a given Point lies in the corresponding quadrant and one that provides a string representation for the predicate that is used for error messages:

import eu.timepit.refined.api.Validate

implicit val quadrant1Validate: Validate.Plain[Point, Quadrant1] =
  Validate.fromPredicate(p => p.x >= 0 && p.y >= 0, p => s"($p is in quadrant 1)", Quadrant1())

implicit val quadrant2Validate: Validate.Plain[Point, Quadrant2] =
  Validate.fromPredicate(p => p.x < 0 && p.y >= 0, p => s"($p is in quadrant 2)", Quadrant2())

implicit val quadrant3Validate: Validate.Plain[Point, Quadrant3] =
  Validate.fromPredicate(p => p.x < 0 && p.y < 0, p => s"($p is in quadrant 3)", Quadrant3())

implicit val quadrant4Validate: Validate.Plain[Point, Quadrant4] =
  Validate.fromPredicate(p => p.x >= 0 && p.y < 0, p => s"($p is in quadrant 4)", Quadrant4())

We have now everything in place to refine Point values with the refineV function and our predicates:

scala> import eu.timepit.refined.refineV
import eu.timepit.refined.refineV

scala> refineV[Quadrant1](Point(1, 3))
res0: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant1]] = Right(Point(1,3))

scala> refineV[Quadrant1](Point(3, -2))
res1: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant1]] = Left(Predicate failed: (Point(3,-2) is in quadrant 1).)

scala> refineV[Quadrant4](Point(3, -2))
res2: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant4]] = Right(Point(3,-2))

We can also use refined's higher order predicates, which take other predicates as arguments, with our quadrant predicates (without defining corresponding Validate instances):

scala> import eu.timepit.refined.boolean.Not
import eu.timepit.refined.boolean.Not

scala> refineV[Not[Quadrant1]](Point(-3, -9))
res3: Either[String,eu.timepit.refined.api.Refined[Point,eu.timepit.refined.boolean.Not[Quadrant1]]] = Right(Point(-3,-9))

scala> refineV[Not[Quadrant1]](Point(5, 4))
res4: Either[String,eu.timepit.refined.api.Refined[Point,eu.timepit.refined.boolean.Not[Quadrant1]]] = Left(Predicate (Point(5,4) is in quadrant 1) did not fail.)

scala> import eu.timepit.refined.boolean.Or
import eu.timepit.refined.boolean.Or

scala> type Quadrant1Or3 = Quadrant1 Or Quadrant3
defined type alias Quadrant1Or3

scala> refineV[Quadrant1Or3](Point(1, 3))
res5: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant1Or3]] = Right(Point(1,3))

scala> refineV[Quadrant1Or3](Point(-3, -2))
res6: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant1Or3]] = Right(Point(-3,-2))

scala> refineV[Quadrant1Or3](Point(3, -2))
res7: Either[String,eu.timepit.refined.api.Refined[Point,Quadrant1Or3]] = Left(Both predicates of ((Point(3,-2) is in quadrant 1) || (Point(3,-2) is in quadrant 3)) failed. Left: Predicate failed: (Point(3,-2) is in quadrant 1). Right: Predicate failed: (Point(3,-2) is in quadrant 3).)