Hi! I’ve been playing around with Validated, and b...
# arrow
i
Hi! I’ve been playing around with Validated, and based on advice here, wrote a simple little function that will return a ValidatedNel based on a single validation rule:
Copy code
suspend fun String.parseAgeString(): ValidatedNel<ValidationError, Int> =
        this.parseToInt().mapLeft{ Nel.just(ValidationError.InvalidAge) }
But I’m having a hard time trying to figure out how to chain additional validation rules---say we have a Valid value but it has to be greater than 18 or it returns another distinct ValidationError.
s
I'm unsure what you mean by chaining additional validations. Did you try reading this doc? https://arrow-kt.io/docs/apidocs/arrow-core-data/arrow.core/-validated/ Some of these APIs are being improved, and the release is end of this month. But basically you can combine different kind of
Validated
values using
mapN
which will become
zip
to mimic the APIs of Kotlin Std. So you can do:
Copy code
fun validateAge(age: Int): ValidatedNel<ValidationError, Int> = ...

fun validateName(name: String): ValidatedNel<ValidationError, String> = ...

validateAge(29).zip(validateName("Simon"), ::Pair)
When using
ValidatedNel
there would not even be the need anymore to pass
Semigroup
only when you're doing so for an arbitrary
E
.
I hope that answers your question.
The current API for
zip
is
Validated.applicative(NonEmptyList.semigroup<ValidationError>()).mapN(validateAge(29), validateName("Simon"), ::Pair)
i
Thanks Simon, Sorry, I’ve been digging through the docs, and I know things are kind of a moving target but it’s hard to tell what’s “idiomatic” arrow usage, versus what’s going away. In the case of Validation I was playing with @raulraja’s example from a week or so ago. So currently I’ve got a function that takes two strings and returns a `ValidatedNel<ValidationError, Double>`:
…then the individual validation functions…
I guess where I’m getting hung up is in trying to add additional logic for validation within a “unit” of data (for lack of a better term). So for example, when I parse the age string I want to make sure it’s an int, but also greater than 18. I thought maybe there was a way to further transform the return value based on chaining additional mappings:
Copy code
suspend fun String.parseAgeString(): ValidatedNel<ValidationError, Int> =
        this.parseToInt().mapLeft{ Nel.just(ValidationError.InvalidAgeNotAnInt) }
           .somethingSomething{ age -> age <= 18 }.mapLeft{ Nel.just(ValidationError.InvalidAgeTooYoung }
I’m been down a rabbit-hole, so maybe just overcomplicating things… 🙂
r
@ibcoleman
make sure it's an int, but also greater than 18
that is actually 2
validNel
values that would then be composed independently with one or many more validated values
Given it could potentially be an int but not greater than zero and that is two independent errors you can know instead of loosing information and coallescing the error into just one. I think that is the purpose of ValidatedNel. the target is the data being validated but when a piece of data requires independent validations these are treated also independently.
👍 1
i
Ah, that makes sense; I think I was wondering if there a way to use
result.a
(the validated Int) here to do additional dependent validations, or if you have to parse the Int in
parseAgeString()
then again in
checkMinimumAge
Copy code
val result = ValidatedNel.applicative(accumulator).tupledN(
            age.parseAgeString(),
            numberOfSpeedingTickets.parseTicketCountString()
        ).somethingSomething{ vals -> checkMinimumAge(vals.a) }
s
I think there is an
andThen
function for dependent validation
👍 1
r
Also to do dependent validations you can use soon the
either
block too as
Validated
values have a bind operator there and they are treated just if it was an
Either
👍 1