Module Arrow Exact
Arrow Exact allows you to use Kotlin's type system to enforce exactness of data structures.
Exact allows automatically projecting smart-constructors on a Companion Object. We can for
example easily create a NotBlankString type that is a String that is not blank, leveraging
the Arrow's Raise DSL to ensure the value is not blank.
import arrow.core.raise.Raise
import arrow.exact.Exact
import arrow.exact.ExactError
import arrow.exact.ensure
import kotlin.jvm.JvmInline
@JvmInline
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankString {
ensure(raw.isNotBlank())
return NotBlankString(raw)
}
}
}We can then easily create values of NotBlankString from a String, which returns us a
Either with the ExactError or the NotBlankString. We can also use fromOrNull to get a
nullable value, or fromOrThrow to throw an ExactException.
note: Make sure to define your constructor as private to prevent creating invalid values.
fun example() {
println(NotBlankString.from("Hello"))
println(NotBlankString.from(""))
}The output of the above program is:
Either.Right(NotBlankString(value=Hello))
Either.Left(ExactError(message=Failed condition.))
You can also define Exact by using Kotlin delegation.
@JvmInline
value class NotBlankString private constructor(val value: String) {
companion object : Exact<String, NotBlankString> by Exact({
ensure(it.isNotBlank())
NotBlankString(it)
})
}You can define a second type NotBlankTrimmedString that is a NotBlankString that is also
trimmed. ensureExact allows us to compose Exact instances and easily
reuse the NotBlankString type.
@JvmInline
value class NotBlankTrimmedString private constructor(val value: String) {
companion object : Exact<String, NotBlankTrimmedString> {
override fun Raise<ExactError>.spec(raw: String): NotBlankTrimmedString {
ensure(raw, NotBlankString)
return NotBlankTrimmedString(raw.trim())
}
}
}