I'm having some trouble understanding the validation library, io.vavr.control.Validation
. At the risk of asking too broad a question, I do have several sub-questions—however I believe they are closely related and would piece together to help me understand the proper way to use this validation mechanism.
I started with the example here: https://softwaremill.com/javaslang-data-validation.
Validation<String, ValidRegistrationRequest> validate(RegistrationRequest request) {
return combine(
validateCardId(request.getCardId()),
validateTicketType(request.getTicketType()),
validateGuestId(request.getGuestId())
)
.ap(ValidRegistrationRequest::new)
.mapError(this::errorsAsJson);
}
private Validation<String, Card> validateCardId(String cardId) {
// validate cardId
// if correct then return an instance of entity the cardId corresponds to
}
private Validation<String, TicketType> validateTicketType(String ticketType) {
// validate ticketType
// if known then return enumeration representing the ticket
}
private Validation<String, Guest> validateGuest(String guestId) {
// validate guestId
// if correct then return an instance of entity the questId corresponds to
}
At first, I didn't understand where the generic parameters for Validation<String, ValidRegistrationRequest>
came from. I now understand that they are linked to the return types of the methods passed to mapError
and ap
, respectively. But:
How does
combine
know to returnValidation<String, ValidRegistrationRequest>
? I feel the only way this is possible, is ifcombine
is actually aValidation<String, ValidRegistrationRequest>::combine
, so that theap
andmapError
are defined from this template. But I don't believe that the compiler should be able to imply that thatcombine
refers to a static implementation in the class of the return type. What's happening here?[Minor] What is the use case for using a
ValidRegistrationRequest
as opposed to justRegistrationRequest
again? I'm tempted to do the latter in my coding, until I see an example.
A second example I was reading about is here: http://www.vavr.io/vavr-docs/#_validation.
class PersonValidator {
private static final String VALID_NAME_CHARS = "[a-zA-Z ]";
private static final int MIN_AGE = 0;
public Validation<Seq<String>, Person> validatePerson(String name, int age) {
return Validation.combine(validateName(name), validateAge(age)).ap(Person::new);
}
private Validation<String, String> validateName(String name) {
return CharSeq.of(name).replaceAll(VALID_NAME_CHARS, "").transform(seq -> seq.isEmpty()
? Validation.valid(name)
: Validation.invalid("Name contains invalid characters: '"
+ seq.distinct().sorted() + "'"));
}
private Validation<String, Integer> validateAge(int age) {
return age < MIN_AGE
? Validation.invalid("Age must be at least " + MIN_AGE)
: Validation.valid(age);
}
}
Where did
Seq
come from? Is that the default when nomapError
is supplied? But I'm looking at the decompiled .class file for Validation.class, and the only reference toSeq
is here:static <E, T> Validation<List<E>, Seq<T>> sequence(Iterable<? extends Validation<List<E>, T>> values) { Objects.requireNonNull(values, "values is null"); List<E> errors = List.empty(); List<T> list = List.empty(); Iterator var3 = values.iterator(); while(var3.hasNext()) { Validation<List<E>, T> value = (Validation)var3.next(); if (value.isInvalid()) { errors = errors.prependAll(((List)value.getError()).reverse()); } else if (errors.isEmpty()) { list = list.prepend(value.get()); } } return errors.isEmpty() ? valid(list.reverse()) : invalid(errors.reverse()); }
Which, I don't think is relevant. Perhaps I'm using an outdated
Validation
? (It is after alljavaslang.control.Validation
in my imports, notio.vavr.control.Validation
.)I had this question for both examples: How does
combine
know which parameters to pass to the constructor (ap
), and in what order? Is the answer, "All its parameters, in the order given"?
Thanks in advance.