-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Validation using Applicative Functor #119
Comments
Thank you for your proposal.
|
Hello @making thank you for your kind answer. I will answer your points:
Once having "programmatic" validation and composition together, in the long term I see the possibility of creating a library containing a set of reusable components like Email, Url, PhoneNumber, NotEmptyString, String3, String50... StringN etc... that could even set has a "functional" alternative to jsr 303. |
@lucapiccinelli I'm positive about adding something similar to Vavr's |
Because YAVI is before 1.0, breaking API change could happen. |
@lucapiccinelli Validator<Email> emailValidator = ...;
Validator<PhoneNumber> phoneNumberValidator = ...;
Validation<ConstraintViolations, Email> emailValidation = emailValidator.prefixed("email").applicative().validate(email);
Validation<ConstraintViolations, PhoneNumber> phoneNumberValidation = phoneNumberValidator.prefixed("phoneNumber").applicative().validate(phoneNumber);
Validation<ConstraintViolation, ContactInfo> contactInfoValidation = emailValidation
.compose(phoneNumberValidation)
.apply(ContactInfo:new);
// or
Validation<ConstraintViolation, ContactInfo> contactInfoValidation = Validations.apply(phoneNumberValidation, phoneNumberValidation, ContactInfo:new);
Either<List<ConstraintViolation>, ContactInfo> either = contactInfoValidation.toEither(); |
@lucapiccinelli I made a pull request (#121) to add |
Yes thank you. Sorry for the latency. I will tomorrow in the morning! |
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); |
Well, that's great. I'm impressed with how quickly you did it. I think that this closes the need that I raised with this issue. Although I would have been very happy to provide an integration of our libraries 😆. Anyway, this is always something that I can do by myself with a dedicated repo 😁. |
If you don't mind I would like to let you think about two possible future improvements to this new API.
curry(ContactInfo:new)
.apply(emailValidator.composable().validate())
.apply(phoneValidator.composable().validate()) In my mind, this should lower the cognitive load of the user of the API. Thank you for your great work 😄 |
If you are okay to keep it well maintained, I'll accept the pr that adds Konado as an optional dependency and the kotlin extension to validator.konado().validate(...) I'm also planning to deprecate |
Yeah, I don't want users to pull the processor in the yavi jar which is useless in the user code.
If you are talking about Actually, I considered
This looks an interesting approach. I'll give it a try. |
BTW, I have changed |
This commit introduces an implementation similar to Vavr's Validation or Scalaz's Validation control class that is an applicative functor and facilitates accumulating errors. closes gh-119
YAVI 0.6.0 has been released though it's not yet synced with Maven Central 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); |
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:
Build a
ConcactsInfo
like in the example is not possible asEither
lacks of aflatMap
method. But also ifflatMap
was there, it would short-circuit onemail
, and then we would miss an eventual error onphone
, in the list of ConstraintViolations.I see only two ways out, in the current version of Yavi:
Email
andPhoneNumber
and having theof
factory method return theConstraintValidations
, by callingvalidate
instead ofvalidateToEither
. Then, every time check the value of theisValid
property ofConstraintValidations
. This solution is not explicit, neither type-safe, and drives to duplication.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
With the
Validation
from Konad the example above would becomeThe text was updated successfully, but these errors were encountered: