io.vavr.control.Validation 在这些示例中如何工作?
How does io.vavr.control.Validation work in these examples?
我在理解验证库时遇到了一些问题,io.vavr.control.Validation
。冒着问得太宽泛的问题的风险,我确实有几个子问题——但我相信它们密切相关,可以拼凑起来帮助我理解使用这种验证机制的正确方法。
我从这里的例子开始: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
}
一开始不明白Validation<String, ValidRegistrationRequest>
的泛型参数是从哪里来的。我现在明白它们分别链接到传递给 mapError
和 ap
的方法的 return 类型。但是:
combine
怎么知道returnValidation<String, ValidRegistrationRequest>
?我觉得这是唯一可能的方法,如果 combine
实际上是一个 Validation<String, ValidRegistrationRequest>::combine
,那么 ap
和 mapError
就是从这个模板定义的。但我不认为编译器应该能够暗示 combine
指的是 return 类型的 class 中的静态实现。这里发生了什么?
[次要] 使用 ValidRegistrationRequest
而不是再次使用 RegistrationRequest
的用例是什么?在我看到一个例子之前,我很想在我的编码中做后者。
我读到的第二个例子在这里: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);
}
}
Seq
从哪里来的?这是没有提供 mapError
时的默认值吗?但是我正在查看 Validation.class 的反编译 .class 文件,并且 only 对 Seq
的引用在这里:
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());
}
我认为这不相关。也许我使用的是过时的 Validation
? (毕竟我的进口是javaslang.control.Validation
,不是io.vavr.control.Validation
。)
我对这两个示例都有这样的疑问:combine
如何知道要将哪些参数传递给构造函数 (ap
),以及传递的顺序是什么?是答案,"All its parameters, in the order given"?
提前致谢。
你和我第一次寻找Vavr的验证机制时有同样的问题和疑惑。
以下是我对前两个问题的回答:
combine(...)
方法 returns 带有一个验证生成器的实例,在本例中,这是一个 Builder3
class 持有 [=13= 的三个结果] 职能。 ap(...)
方法是此构建器的一个方法,会触发构建 Validation
个实例。
调用时,验证结果将一个一个地应用到作为参数提供的函数的柯里化版本:
v3.ap(v2.ap(v1.ap(Validation.valid(f.curried()))))
在示例中,f
是 ValidRegistrationRequest
class 的构造函数。最后,我们有一个包含有效请求实例的验证。
另一方面,如果任何结果无效,该方法会创建一个包含错误消息列表的无效结果。并调用 mapError(this::errorsAsJson)
(这次在 Validation
实例上!)将其转换为 JSON 格式。
- 使用
ValidRegistrationRequest
的用例是什么?
我在我的一个项目中使用了 Vavr 的验证。我收到了一个带有一些实体标识符的请求。为了验证它的正确性,我不得不查询一个数据库来检查每个id是否都有。
因此,如果 return 对原始请求进行验证,我将不得不再次从数据库中获取这些对象。因此,我决定 return ValidRegistrationRequest
持有域对象。只调用一次数据库,请求处理明显更快。
以及第二对问题的答案:
- 是的,你是对的。在无效结果的情况下,
Validation.combine(...).ap(...)
return 带有 Invalid
class 的实例,其中包含错误消息列表,return 来自验证方法。
如果您查看来源,使用 Validation.ap(...)
方法,您可以看到无效结果被收集到 Vavr 的 List
中。因为继承自Seq
,所以在validatePerson
例子中可以看到这个类型Seq<String>
.
- 是的,没错。 "All its parameters, in the order given":)
combine
中的参数顺序必须与提供给 ap(...)
方法的函数采用的参数顺序相同。
下载源代码后,跟踪 Vavr 的内部结构就容易多了。
好的,这是我尝试回答我自己的问题,但最好能得到更有经验的人的确认。我找到了验证 here.
的最新来源
示例 1
我从中复制示例的文章实际上说 combine
是 "statically imported for better readability." 我错过了。所以,我是对的——我们正在调用一个静态方法。具体来说,这个:
static <E, T1, T2, T3> Builder3<E, T1, T2, T3> combine(Validation<E, T1> validation1, Validation<E, T2> validation2, Validation<E, T3> validation3) {
Objects.requireNonNull(validation1, "validation1 is null");
Objects.requireNonNull(validation2, "validation2 is null");
Objects.requireNonNull(validation3, "validation3 is null");
return new Builder3<>(validation1, validation2, validation3);
}
我对 ValidRegistrationRequest
的使用的猜测只是为了在编译时强制执行验证。也就是说,通过这种方式,如果所有使用代码都需要 ValidRegistrationRequest
.
,则开发人员绝不会意外使用未经验证的 RegistrationRequest
示例 2
我认为 Set
来自这里:
/**
* An invalid Validation
*
* @param <E> type of the errors of this Validation
* @param <T> type of the value of this Validation
*/
final class Invalid<E, T> implements Validation<E, T>, Serializable {
...
@Override
public Seq<E> getErrors() {
return errors;
}
...
}
然后与此相关的事情:
/**
* Applies a given {@code Validation} that encapsulates a function to this {@code Validation}'s value or combines both errors.
*
* @param validation a function that transforms this value (on the 'sunny path')
* @param <U> the new value type
* @return a new {@code Validation} that contains a transformed value or combined errors.
*/
@SuppressWarnings("unchecked")
default <U> Validation<E, U> ap(Validation<E, ? extends Function<? super T, ? extends U>> validation) {
Objects.requireNonNull(validation, "validation is null");
if (isValid()) {
return validation.map(f -> f.apply(get()));
} else if (validation.isValid()) {
return (Validation<E, U>) this;
} else {
return invalidAll(getErrors().prependAll(validation.getErrors()));
}
}
- @mchmiel 在我写我的时候回答了我的问题。
我在理解验证库时遇到了一些问题,io.vavr.control.Validation
。冒着问得太宽泛的问题的风险,我确实有几个子问题——但我相信它们密切相关,可以拼凑起来帮助我理解使用这种验证机制的正确方法。
我从这里的例子开始: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
}
一开始不明白Validation<String, ValidRegistrationRequest>
的泛型参数是从哪里来的。我现在明白它们分别链接到传递给 mapError
和 ap
的方法的 return 类型。但是:
combine
怎么知道returnValidation<String, ValidRegistrationRequest>
?我觉得这是唯一可能的方法,如果combine
实际上是一个Validation<String, ValidRegistrationRequest>::combine
,那么ap
和mapError
就是从这个模板定义的。但我不认为编译器应该能够暗示combine
指的是 return 类型的 class 中的静态实现。这里发生了什么?[次要] 使用
ValidRegistrationRequest
而不是再次使用RegistrationRequest
的用例是什么?在我看到一个例子之前,我很想在我的编码中做后者。
我读到的第二个例子在这里: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);
}
}
Seq
从哪里来的?这是没有提供mapError
时的默认值吗?但是我正在查看 Validation.class 的反编译 .class 文件,并且 only 对Seq
的引用在这里: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()); }
我认为这不相关。也许我使用的是过时的
Validation
? (毕竟我的进口是javaslang.control.Validation
,不是io.vavr.control.Validation
。)我对这两个示例都有这样的疑问:
combine
如何知道要将哪些参数传递给构造函数 (ap
),以及传递的顺序是什么?是答案,"All its parameters, in the order given"?
提前致谢。
你和我第一次寻找Vavr的验证机制时有同样的问题和疑惑。
以下是我对前两个问题的回答:
combine(...)
方法 returns 带有一个验证生成器的实例,在本例中,这是一个Builder3
class 持有 [=13= 的三个结果] 职能。ap(...)
方法是此构建器的一个方法,会触发构建Validation
个实例。
调用时,验证结果将一个一个地应用到作为参数提供的函数的柯里化版本:
v3.ap(v2.ap(v1.ap(Validation.valid(f.curried()))))
在示例中,f
是 ValidRegistrationRequest
class 的构造函数。最后,我们有一个包含有效请求实例的验证。
另一方面,如果任何结果无效,该方法会创建一个包含错误消息列表的无效结果。并调用 mapError(this::errorsAsJson)
(这次在 Validation
实例上!)将其转换为 JSON 格式。
- 使用
ValidRegistrationRequest
的用例是什么?
我在我的一个项目中使用了 Vavr 的验证。我收到了一个带有一些实体标识符的请求。为了验证它的正确性,我不得不查询一个数据库来检查每个id是否都有。
因此,如果 return 对原始请求进行验证,我将不得不再次从数据库中获取这些对象。因此,我决定 return ValidRegistrationRequest
持有域对象。只调用一次数据库,请求处理明显更快。
以及第二对问题的答案:
- 是的,你是对的。在无效结果的情况下,
Validation.combine(...).ap(...)
return 带有Invalid
class 的实例,其中包含错误消息列表,return 来自验证方法。
如果您查看来源,使用 Validation.ap(...)
方法,您可以看到无效结果被收集到 Vavr 的 List
中。因为继承自Seq
,所以在validatePerson
例子中可以看到这个类型Seq<String>
.
- 是的,没错。 "All its parameters, in the order given":)
combine
中的参数顺序必须与提供给 ap(...)
方法的函数采用的参数顺序相同。
下载源代码后,跟踪 Vavr 的内部结构就容易多了。
好的,这是我尝试回答我自己的问题,但最好能得到更有经验的人的确认。我找到了验证 here.
的最新来源示例 1
我从中复制示例的文章实际上说
combine
是 "statically imported for better readability." 我错过了。所以,我是对的——我们正在调用一个静态方法。具体来说,这个:static <E, T1, T2, T3> Builder3<E, T1, T2, T3> combine(Validation<E, T1> validation1, Validation<E, T2> validation2, Validation<E, T3> validation3) { Objects.requireNonNull(validation1, "validation1 is null"); Objects.requireNonNull(validation2, "validation2 is null"); Objects.requireNonNull(validation3, "validation3 is null"); return new Builder3<>(validation1, validation2, validation3); }
我对
ValidRegistrationRequest
的使用的猜测只是为了在编译时强制执行验证。也就是说,通过这种方式,如果所有使用代码都需要ValidRegistrationRequest
. ,则开发人员绝不会意外使用未经验证的
RegistrationRequest
示例 2
我认为
Set
来自这里:/** * An invalid Validation * * @param <E> type of the errors of this Validation * @param <T> type of the value of this Validation */ final class Invalid<E, T> implements Validation<E, T>, Serializable { ... @Override public Seq<E> getErrors() { return errors; } ... }
然后与此相关的事情:
/** * Applies a given {@code Validation} that encapsulates a function to this {@code Validation}'s value or combines both errors. * * @param validation a function that transforms this value (on the 'sunny path') * @param <U> the new value type * @return a new {@code Validation} that contains a transformed value or combined errors. */ @SuppressWarnings("unchecked") default <U> Validation<E, U> ap(Validation<E, ? extends Function<? super T, ? extends U>> validation) { Objects.requireNonNull(validation, "validation is null"); if (isValid()) { return validation.map(f -> f.apply(get())); } else if (validation.isValid()) { return (Validation<E, U>) this; } else { return invalidAll(getErrors().prependAll(validation.getErrors())); } }
- @mchmiel 在我写我的时候回答了我的问题。