Speaking of validations, here's a small editorial ...
# arrow
y
Speaking of validations, here's a small editorial and suggested changes to the Error Handling Tutorial to promote what I would consider a slightly better practice with regards to which return type to use for validation functions (
Validated
vs.
ValidatedNel
). The tutorial and most (or even all) examples I've seen on using the
Validated
type declare all validation functions using
ValidatedNel
type, even if the function only really ever returns a single error. For instance, a string field validation function that checks the length will either return a (single) length validation error, or the valid string, but the error is wrapped in the
Nel
. And the only reason I can think of is because when zipping the validated types together, one can simply do
Copy code
val someEntity: Validated<DomainError, SomeEntity> = 
  validateField1()
    .zip(SemiGroup.nonEmptyList(), 
         validateField2(), 
         validateField3()) { (v1, v2, v3) -> SomeEntity(v1, v2, v3) }
i.e. not worry about dealing with
Validated
vs.
ValidatedNel
. I personally think the semantics are not accurate for these validation functions as we're false advertising that there could be multiple errors returned by each validation function. I think we should use the
Nel
type when the function can truly return multiple errors, otherwise you should stick with
Validated
. When zipping all your validated types together, it's really trivial to "sprinkle" a little
toValidatedNel
here and there to then accumulate all the errors together:
Copy code
val someEntity: Validated<DomainError, SomeEntity> = 
  validateField1SingleError().toValidatedNel()
    .zip(SemiGroup.nonEmptyList(), 
         validateField2MultipleErrors(), 
         validateField3SingleError().toValidatedNel()) { (v1, v2, v3) -> SomeEntity(v1, v2, v3) }
Really nitpicking here, but I'm just curious to get other people's thoughts. Thanks!
👍 2
s
Hey Yannick, Thanks for this feedback! I think that is absolutely valid, and a good suggestion. I've been back-and-forth a bit about this myself. I always find myself using
ValidatedNel
and I also typically only have a single error in my actual validation function. Also I think that explicitly converting is not bad perse. Perhaps we can also expose
zipNel
that allows passing
Validated
but returns
ValidatedNel
. If you're up for making a ticket on Github that would be really appreciated 🙏 For Arrow 2.0 we're also thinking about the docs, but reducing the API and improving API will really help is improve the docs in a major way I think.
y
Sure, I'll open an issue!
What about taking it a step further and saying that instead of declaring said functions that only return a single error with the return type of
Validated<SomeErrorAdt, A>
, it should be
Validated<SpecificErrorAdtInstance, A>
? So from the error handling tutorial, it would be
Copy code
private fun FormField.contains(needle: String): Validated<DoesNotContain, FormField> =
    if (value.contains(needle, false)) valid()
    else ValidationError.DoesNotContain(needle).invalid()
instead of
Copy code
private fun FormField.contains(needle: String): ValidatedNel<ValidationError, FormField> =
    if (value.contains(needle, false)) validNel()
    else ValidationError.DoesNotContain(needle).invalidNel()
? That's even more closer to what the function actually does. It doesn't just return any of the
ValidationError
, it can only return the
DoesNotContain
error. You can still compose that with any other validation functions whose return type either use
ValidationError
because they can truly possibly return a subset of the subclasses, or with functions that also use another specific subclass.
s
Yes!! That is indeed a good suggestion for the docs as well. This is also what I do in my codebases, and examples. https://github.com/nomisRev/ktor-arrow-example/blob/08aef69ded5153938f6135c8e282bf87bad9bf76/src/main/kotlin/io/github/nomisrev/validation.kt#L119
y
Cool. I'll incorporate that as well. Ah yes, that's the codebase for the "Building applications with Kotlin and Arrow.kt in style" Youtube video that I haven't finished watching yet 😉