I started with the evaluation on how traditional e...
# arrow
p
I started with the evaluation on how traditional exception handling using try-catch can be migrated to using
Either
. What is the correct way to use
Either
in combination with an init-Block of an data class?
Copy code
data classUser (
    val id: Identity,
    val firstName: String,
    val lastName: String,
    val birthDate: LocalDate,
    val address: Address,
) {
    init {
        require(firstName.isNotEmpty()){
           "FirstName must not be empty."
        }
    }
}
Is it the correct way to migrate the data class to a class with a private constructor and a builder function that returns an
Either
?
r
Hi, yes, you can do it that way, but I would suggest creating a separate type for firstName, like
Name
or
NotBlankString
that will hold a notBlank requirement. This class can have a factory method like this:
Name.from(str: String): Either<Blank, Name>
And User class then will look like:
Copy code
data class User (
    val id: Identity,
    val firstName: Name, // here we are sure that Name is not blank
    val lastName: String,
    val birthDate: LocalDate,
    val address: Address,
)
s
With the help of @Ruslan Ustits, and @Iliyan Germanov and feedback from @CLOVIS we’re experimenting to create a small DSL for this pattern. https://github.com/arrow-kt/arrow-exact
r
Btw, there is a good article by @simon.vergauwen about such modeling on arrow website https://arrow-kt.io/learn/design/domain-modeling/
p
Thanks for your valuable approaches. That means I create types for low level types like strings or integers and move the validation there and provide factories for the creation? But does this not just move the problem to an other place? What if there are more complex validations like dependencies of multiple properties? In my feeling this will get a lot more complex than the initial version.
s
Yes, and no. It depends on what you want. Using
Either
you can easily accumulate errors to figure out all the different things that went wrong to initialise your types. Or you short-circuit, which is the same as
require
but using
ensure
and returning an
Either
instead of throwing an exception. You’re free to model it however you care about.
i
@PoisonedYouth haven't tried it for more complex potentially
suspend
validations. @simon.vergauwen @Ruslan Ustits we might consider adding
suspend from(value: A): Either
. My take is that the earlier you validate your domain data, the earlier you'll have guarantees with your types. TL;DR; • No Arrow Exact + primitive
Int
• => you'll need to validate every single function that accepts
Int
• With Arrow Exaxt and custom
Quantity
=> you'll need to validate quantity only once and every function down the chain that accepts
Quantity
won't do validation. •
Either
is powerful monad and when combined with
Raise
you can do whatever you want • +1 for Simon's and Ruslan's point
p
I think what I can take away is, that it's not a 1:1 replacement between the traditional try-catch exception handling and the Either way. Some workflow needs to be changed to better match.