question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Support Validation using Applicative Functor

See original GitHub issue

Hey there, that’s a great library! But I see an issue, similar to the same issue that affects validation via annotations. It is not that simple to define a reusable Value Object class, that is itself composed by other Value Object classes.

Example in Kotlin:

data class Email private constructor(val value: String){
   companion object {
       fun of(value: String): Either<ConstraintViolations, Email> = ValidatorBuilder.of<Email>()
            .konstraint(Email::value) {
                email()
            }.build()
            .validateToEither(Email(emailValue))
   }
}
data class Phone private constructor(val value: String){
   ... similar factory method here
}
data class ContactsInfo(val email: Email, val phone: PhoneNumber){
   companion object {
       fun of(emailValue: String, phoneValue: String): Either<ConstraintViolations, ContactsInfo>{
          val email: Either<ConstraintViolations, Email> = Email.of(emailValue)
          val phone: Either<ConstraintViolations, PhoneNumber> = PhoneNumber.of(phoneValue)
          
          ... How to compose email and phone here to build a ContactsInfo()? ...
       }
   }
}

Build a ConcactsInfo like in the example is not possible as Either lacks of a flatMap method. But also if flatMap was there, it would short-circuit on email, and then we would miss an eventual error on phone, in the list of ConstraintViolations.

I see only two ways out, in the current version of Yavi:

  1. making public the constructors of Email and PhoneNumber and having the of factory method return the ConstraintValidations, by calling validate instead of validateToEither. Then, every time check the value of the isValid property of ConstraintValidations. This solution is not explicit, neither type-safe, and drives to duplication.
  2. Use exceptions.

My solution proposal is: let’s discuss a way of integrating Yavi with the Validation Monad of Konad in order to have an extension method like

fun <T> Validator<T>.validateToKonad(target: T): Validation<ConstraintViolations, T> =
    validate(target).run {
        if(isValid) target.success()
        else fail()
    }

With the Validation from Konad the example above would become

data class ContactsInfo(val email: Email, val phone: PhoneNumber){
   companion object {
       fun of(emailValue: String, phoneValue: String): Validation<ConstraintViolations, ContactsInfo> =
           ::ContactsInfo + Email.of(emailValue) + PhoneNumber.of(phoneValue)
   }
}

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:14 (10 by maintainers)

github_iconTop GitHub Comments

2reactions
makingcommented, May 13, 2021

BTW, I think YAVI’s Arguments Validator can achieve the same outcome though it’s more verbose:

Validator<Email> emailValidator = ...;
Validator<PhoneNumber> phoneNumberValidator = ...;

Arguments2Validator<Email, PhoneNumber, ContactInfo> contactInfoValidator = ArgumentsValidatorBuilder.of(ContactInfo::new)
    .builder(b -> b.nest(Arguments1::arg1, "email", emailValidator)
                   .nest(Arguments2::args, "phoneNumber", phoneNumberValidator))
    .build();

Either<ConstraintViolations, ContactInfo> either = contactInfoValidator.validateArgs(email, phoneNumber);
1reaction
makingcommented, May 20, 2021

YAVI 0.6.0 has been released though it’s not yet synced with Maven Central https://github.com/making/yavi/releases/tag/0.6.0

Final form looks like

Validator<Email> emailValidator = ...;
Validator<PhoneNumber> phoneNumberValidator = ...;

Validated<Email> emailValidated = emailValidator.applicative().validate(email);
Validated<PhoneNumber> phoneNumberValidated = phoneNumberValidator.applicative().validate(phoneNumber);

Validated<ContactInfo> contactInfoValidated = emailValidated.combine(phoneNumberValidated)
            .apply((em, ph) -> new ContactInfo(em, ph));
// or
Validated<ContactInfo> contactInfoValidated = Validations.combine(emailValidated, phoneNumberValidation)
		.apply((em, ph) -> new ContactInfo(em, ph));

boolean isValid = contactInfoValidated.isValid();

ContactInfo contactInfo = contactInfoValidated
        		.orElseThrow(violations -> new ConstraintViolationsException(violation));

HttpStatus status = contactInfoValidated
        		.fold(violations -> HttpStatus.BAD_REQUEST, contactInfo -> HttpStatus.OK);
Read more comments on GitHub >

github_iconTop Results From Across the Web

How an applicative functor can help us validate forms | Devlog
We will 'play' with applicative functors. To be more specific we will use it to validate some user input that comes from a...
Read more >
Applicative validation - ploeh blog
In this article you saw how to validate a complex data type, collecting and reporting on all problems, if any.
Read more >
Applicative Functors and data validation, part II - Codurance
Either is a general purpose data type. Validation is a variation of Either exclusively for validation purposes that accumulates all the errors.
Read more >
Applicative Validation - PureScript by Example
There is another applicative functor which is provided by the validation library. This functor is called V , and it provides the ability...
Read more >
Applicative Functors for the Java Programmer - Naveen Muguda
Applicatives are a friendlier alternative to monads in some scenarios like the promise parallelization and elaborate validation. Knowledge of applicatives can ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found