Custom Type
Import
import refined4s.*
Define Refined
Type
type RefinedTypeName = RefinedTypeName.Type
object RefinedTypeName extends Refined[ActualType] {
override inline def invalidReason(a: ActualType): String =
expectedMessage("something with blah blah")
override inline def predicate(a: ActualType): Boolean =
// validation logic here
}
e.g.)
type MyString = MyString.Type
object MyString extends Refined[String] {
override inline def invalidReason(a: String): String =
expectedMessage("a non-empty String")
override inline def predicate(a: String): Boolean =
a != ""
}
Create Value
Given the following Refined
type,
type Month = Month.Type
object Month extends Refined[Int] {
override inline def invalidReason(a: Int): String =
expectedMessage("Int between 1 and 12 (1 - 12)")
override inline def predicate(a: Int): Boolean =
a >= 1 && a <= 12
}
With Compile-time Validation
If the actual value is a constant value meaning that the actual value is known in compile-time
(e.g. number, Boolean, String literals), Refined
provides a compile-time validation
with its apply
method.
Valid cases
Month(1)
// res1: Type = 1
Month(12)
// res2: Type = 12
Invalid cases
Month(0)
// Month(0)
// ^^^^^^^^
// Invalid value: [0]. It must be Int between 1 and 12 (1 - 12)
Month(13)
// Month(13)
// ^^^^^^^^^
// Invalid value: [13]. It must be Int between 1 and 12 (1 - 12)
With Runtime Validation
Valid cases
val monthInput1 = 1
// monthInput1: Int = 1
val monthInput2 = 12
// monthInput2: Int = 12
Month.from(monthInput1)
// res3: Either[String, Type] = Right(value = 1)
Month.from(monthInput2)
// res4: Either[String, Type] = Right(value = 12)
Invalid cases
val invalidMonthInput1 = 0
// invalidMonthInput1: Int = 0
val invalidMonthInput2 = 13
// invalidMonthInput2: Int = 13
Month.from(invalidMonthInput1)
// res5: Either[String, Type] = Left(
// value = "Invalid value: [0]. It must be Int between 1 and 12 (1 - 12)"
// )
Month.from(invalidMonthInput2)
// res6: Either[String, Type] = Left(
// value = "Invalid value: [13]. It must be Int between 1 and 12 (1 - 12)"
// )
Get Actual Value
To get the actual value you can simply use the value
method.
val month = Month(1)
// month: Type = 1
month.value
// res7: Int = 1
Pattern Matching
For pattern matching, Refined
has built-in unapply
so you can simply do
month match {
case Month(value) =>
println(s"Pattern matched value: $value")
}
// Pattern matched value: 1