Hi all, I would like to use the concept of "error...
# arrow
a
Hi all, I would like to use the concept of "errors as values" instead of throwing exceptions when I validate the data of a newly instantiated class. Before, to ensure my data was validated I would use the
init{ }
block and this would throw an exception if the data given in the constructor was invalid. Example:
Copy code
@JvmInline
value class Foo(
    val bar: String,
) {
    init {
        require(bar.isNotBlank())
    }
}
I was thinking I could use the
Either
class and a companion object to validate the data when calling
create
without throwing an exception.
Copy code
@JvmInline
value class Foo(
    val bar: String,
) {
    companion object {
        fun create(bar: String): Either<Error, Foo> =
            if (bar.isNotBlank()) {
                Foo(bar).right()
            } else {
                IsBlankString().left()
            }
        }
    }
}

sealed interface Error {
    class IsBlankString: Error
}
The issue is that I can't make the
Foo
constructor
Foo(val bar: String)
private for
value class
or
data class
. So I can't guarantee that the property
bar
has been validated if someone creates
Foo
with
Foo("")
instead of
Foo.create("")
. I tried changing the function name from
create
to
invoke
to possibly override the constructor but it seems the Kotlin compiler picks the constructor
Foo(val bar: String)
instead of the
invoke
function when calling
Foo("")
. I understand I can achieve this with a regular
class
and use a private constructor, but I haven't been able to achieve this with a
value class
or
data class
. I would like have the auto-generated
copy()
and
equals()
functions that
data classes
provides. I read this article and they have an interesting solution for this issue by using a
sealed interface
to validate a
data class
. When I tried this solution, I wasn't able to use the
.copy()
function normally auto-generated for
data class
. So there are some tradeoffs. Link: https://proandroiddev.com/how-to-use-arrows-either-for-exception-handling-in-your-application-a73574b39d07 Does anyone have any recommendations or solutions you have used to solve this issue?
e
a
The issue is that I can't make the
Foo
constructor
Foo(val bar: String)
private for
value class
or
data class
.
why can't you do it? you should be able to do
Copy code
@JvmInline
value class Foo private constructor(
    val bar: String,
) {
    init {
        require(bar.isNotBlank())
    }
}
a
Thank you Edgar for the link to the Akkurate library. If there is not a built-in way to do this I might go that route. Thank you Alejandro for showing how to make the constructor private, this solves my issue for the
value class
. Example solution:
Copy code
@JvmInline
value class Foo private constructor(
    val bar: String,
) {
    companion object {
        operator fun invoke(bar: String): Either<Error, Foo> =
            if (bar.isNotBlank()) {
                Foo(bar).right()
            } else {
                IsBlankString().left()
            }
    }
}

sealed interface Error {
    class IsBlankString : Error
}
For making the
data class
constructor private and using the auto-generated
copy()
function, I see some discussion on this and the Kotlin language recommendations. https://youtrack.jetbrains.com/issue/KT-11914 https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-consistent-copy-visibility/ It looks like in a near future version of Kotlin, if I make the
data class
constructor private, then the auto-generated
copy()
function will be private too. The Kotlin docs for
data class
says
Providing explicit implementations for the
componentN()
and
copy()
functions is not allowed.
https://kotlinlang.org/docs/data-classes.html#properties-declared-in-the-class-body I will have to manually write some function similar to
copy()
and name it something else. Thank you for the help!
a
@Edgar Avuzi akkurate is validating, while you most likely want https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/
gratitude thank you 1
💯 2
e
Artūras, As far as I get it, nothings stops one using Akkurate for "Parse, don't validate" principle like this https://hastebin.com/share/epabakufer.kotlin
💯 1
a
the same principles are applied to Arrow's validation docs https://arrow-kt.io/learn/typed-errors/validation/