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())
}
}
}