Hi, I want to do a nested validation on my input r...
# arrow
p
Hi, I want to do a nested validation on my input request body. I did it as below:
Copy code
fun InputRequest.validate(): Validated<IncorrectInput, InputRequest> =
    userType.validateMail(obj.inputEnum)
        .zip(username.validateUserName(), obj.validateObj()) {
                userType,
                userName,
                obj ->
            InputRequest(userType, userName, obj)
        }
        .mapLeft(::IncorrectInput)

fun String.validateUserType(enum: Enum):ValidatedNel<InvalidUserType, String> {
    val trimmed = trim()
    if(trimmed.isBlank()) {
        return "User type can't be empty".invalidNel().mapLeft(toInvalidField(::InvalidUserType))
    }
    if(trimmed == "other" && !(enum == Enum.E1 || enum == Enum.E2)) {
        return "Given user type is not supported with E1 and E2".invalidNel().mapLeft(toInvalidField(::InvalidMail))
    }
    return validNel()
}
In the above code,
validateUserType
looks a bit dirty to me. Is there a better to do the same?
m
@Partho Paul looks like you want short circuit behaviour for your validation. In that case you can do that using
eagerEffect { ... }.toValidated()
or
either.eager { ... }.toValidated()
The nice thing about this approach is that we no longer need early returns, because
bind()
and
ensure
manage the short circuits for you.
Copy code
fun String.validatedUserType(enum: Enum): ValidatedNel<InvalidUserType, String> = eagerEffect { // or either.eager 
  val trimmed = trim()
  ensure(trimmed.isNotBlank()) { InvalidUserType("User type can't be empty").nel() }
  if (trimmed == "other") {
    // notice we can implement other validations when trimmed is "other"
    ensure(enum in listOf(Enum.E1, Enum.E2)) { InvalidMail("Given user type is not supported with E1 and E2").nel() }
  }
  trimmed
}.toValidated()
Theoretical reason why we do
either -> validated
transformation: •
Validated<L, R>
by design only has map. it doesn’t (and arguably should not) support short circuits •
Either<L, R>
has both map and flatMap - it support short circuits. <<< we want this In other words, we first need to operate on either, short circuit as necessary, and then transform that either back to validated after that’s all done. The effect builder abstracts this complexity away for end users so they can now focus on solving the problems at hand without having to think about these constraints. I hope that helps!
☝️ 1
p
@mitch Thank you, will try this out.
s
@Partho Paul you can find some more concrete examples and how they're used here. https://github.com/nomisRev/ktor-arrow-example/blob/main/src/main/kotlin/io/github/nomisrev/validation.kt
🙏 2