Hi, I’m trying to accumulate some validation erro...
# arrow
b
Hi, I’m trying to accumulate some validation errors with
zipOrAccumulate
. Something like this:
Copy code
object MyError

fun example(input1: String, input2: String, input3: String): Either<NonEmptyList<MyError>, String> = either {
    zipOrAccumulate(
        { ensure(input1.isNotBlank()) { MyError } },
        { ensure(input2.isNotBlank()) { MyError } },
        { ensure(input3.isNotBlank()) { MyError } }
    ) { _, _, _ ->
        TODO()
    }
}
Everything was smooth sailing until I encountered a model for which I need to do more than 9 validations. I found this post on Stackoverflow, which describes the same issue for the deprecated
zip
function: https://stackoverflow.com/questions/72782045/arrow-validation-more-then-10-fields/72782420#72782420 I’m not sure how to apply this to
zipOrAccumulate
. Any hints on how to handle this would be greatly appreciated! 😊
👍 1
y
Just call zipOrAccumulate inside as well
b
Hi Youssef, Thanks for the swift response. I've tried your suggestion, but I think I'm doing something wrong still. In the sample below I would expect to see 3 accumulated errors (1, 4 and 5), yet I only see the one from the first
zipOrAccumulate
.
Copy code
data class MyError(val name: String)

fun example(input1: String, input2: String, input3: String, input4: String, input5: String, input6: String): Either<NonEmptyList<MyError>, String> = either {
    zipOrAccumulate(
        { ensure(input1.isNotBlank()) { MyError("1") } },
        { ensure(input2.isNotBlank()) { MyError("2") } },
        { ensure(input3.isNotBlank()) { MyError("3") } }
    ) { _, _, _ ->
        zipOrAccumulate(
            { ensure(input4.isNotBlank()) { MyError("4") } },
            { ensure(input5.isNotBlank()) { MyError("5") } },
            { ensure(input6.isNotBlank()) { MyError("6") } }
        ) { _, _, _ ->
            "$input1,$input2,$input3,$input4,$input5,$input6"
        }
    }
}

fun main() {
    val result = example("", "two", "three", "", "", "six")
    println(result)

    // Actual: Either.Left(NonEmptyList(MyError(name=1)))
    // Expected: Either.Left(NonEmptyList(MyError(name=1), MyError(name=4), MyError(name=5)))
}
s
the result of the inner
zioOrAccumulate
should go as one of the inputs of the outer one
a
the problem here is that the latest
zipOrAccumulate
has a different type than the rest: the first ones use a single error, whereas the last one produces a list of errors
b
@stojan I've played around with it, but i'm not sure how to achieve that? @Alejandro Serrano.Mena Yep indeed. Do you know how I can refactor this in such a way it accumulates all the errors? I'm getting the feeling I'm missing some very obvious solution, I just don't see it 🥲
a
I think the most straightforward would be to return a
NonEmptyList
everywhere, and then
flatMap
at the end, something akin to
Copy code
fun example(input1: String, input2: String, input3: String): Either<NonEmptyList<MyError>, String> = either {
    zipOrAccumulate(
        { ensure(input1.isNotBlank()) { MyError.nel() } },
        { ensure(input2.isNotBlank()) { MyError.nel() } },
        { ensure(input3.isNotBlank()) { MyError.nel() } }
    ) { _, _, _ ->
        TODO()
    }
}.mapLeft { it.flatMap { it } }
p
better than
either { .... }.mapLeft { it.flatMap { it } }
you could do
Copy code
zipOrAccumulate(
        { err1, err2 -> err1 + err2 },
        { ensure(input1.isNotBlank()) { MyError.nel() } },
        { ensure(input2.isNotBlank()) { MyError.nel() } },
        { ensure(input3.isNotBlank()) { MyError.nel() } }
    ) { _, _, _ ->
        TODO()
    }
305 Views