arrow-exact
arrow-exact copied to clipboard
Arrow Exact exposes a variation of exact types to work with in Arrow. It includes some boilerplate for commonly defined value class wrappers.
Module Arrow Exact
Arrow Exact allows you to use Kotlin's type system to enforce exactness of data structures.
Introduction
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())
}
}
}