| Did you know ... | Search Documentation: |
| Pack logtalk -- logtalk-3.98.0/library/validations/NOTES.md |
This file is part of Logtalk https://logtalk.org/ SPDX-FileCopyrightText: 1998-2026 Paulo Moura <pmoura@logtalk.org> SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
validations
This library provides an implementation of validation terms with an API
for applicative-style error accumulation. A validation term is an opaque
compound term that either contains a valid value (valid(Value)) or a list
of errors (invalid(Errors)).
The library follows the same design pattern as the optionals and expecteds
libraries:
validation â factory object for constructing validation termsvalidated â companion helper object for list operations, type-checking,
and QuickCheck support
The naming follows the convention used in Scala Cats and Kotlin Arrow, where the companion type is called Validated.
Open the [../../apis/library_index.html#validations](../../apis/library_index.html#validations) link in a web browser.
To load all entities in this library, load the loader.lgt file:
| ?- logtalk_load(validations(loader)).
To test this library predicates, load the tester.lgt file:
| ?- logtalk_load(validations(tester)).
The validation object provides constructors for validation terms. For example:
| ?- validation::of_valid(1, Validation).
...
The created validation terms can then be passed as parameters to the validation/1 parametric object. For example:
| ?- validation::of_valid(1, V1), validation::of_valid(2, V2),
validation::sequence([V1, V2], Validation).
Validation = valid([1,2])
yes
| ?- validation::of_invalid(e1, V1), validation::of_invalid(e2, V2),
validation::sequence([V1, V2], Validation).
Validation = invalid([e1,e2])
yes
| ?- validation(valid(1))::flat_map([X,V]>>(Y is X+1, V = valid(Y)), Validation).
Validation = valid(2)
yes
Validation terms can be constructed from goals, optionals, and expecteds:
| ?- validation::from_goal(succ(1, Value), Value, Validation).
Validation = valid(2)
yes
| ?- validation::from_optional(empty, no_value, Validation).
Validation = invalid([no_value])
yes
| ?- validation::from_expected(unexpected(e), Validation).
Validation = invalid([e])
yes
Validation terms can be converted to optionals and expecteds:
| ?- validation(valid(42))::to_optional(Optional).
Optional = optional(42)
yes
| ?- validation(invalid([e1,e2]))::to_expected(Expected).
Expected = unexpected([e1,e2])
yes
The key feature of validation is error accumulation via zip/3:
| ?- validation::of_valid(1, V1), validation::of_invalid(e1, V2),
validation(V1)::zip([_,_,_]>>true, V2, Result).
Result = invalid([e1])
yes
| ?- validation::of_invalid(e1, V1), validation::of_invalid(e2, V2),
validation(V1)::zip([_,_,_]>>true, V2, Result).
Result = invalid([e1,e2])
yes
The companion validated object provides predicates for handling lists of
validation terms, including valids/2, invalids/2, partition/3, and
map/3-4.
| ?- validation::of_valid(1, V1), validation::of_invalids([e1,e2], V2), validation::of_valid(2, V3),
validated::partition([V1, V2, V3], Values, Errors).
Values = [1,2],
Errors = [e1,e2]
yes
integer(Term) -> validation::of_valid(Term, Validation)
; validation::of_invalid(not_integer(Term), Validation)
), [1,a,2,b], ValuesErrors).
ValuesErrors = [1,2]-[not_integer(a),not_integer(b)]
yes
integer(Term) -> validation::of_valid(Term, Validation)
; validation::of_invalid(not_integer(Term), Validation)
), [1,a,2,b], Values, Errors).
Values = [1,2],
Errors = [not_integer(a),not_integer(b)]
yes
expecteds library
Both this library and the expecteds library wrap computations that may
succeed or fail. The fundamental difference is the error model:
expecteds carries a single error and short-circuits on the
first failure (monadic error handling). Once an unexpected term is
produced, subsequent operations are skipped.validations carries a list of errors and accumulates all
failures (applicative error handling). Operations such as zip/3,
sequence/2, and traverse/3 collect every error.
Use expecteds when you only care about the first error. Use validations when you
want to report all problems at once (e.g. form validation, configuration
checking, or batch input processing).
Consider validating a form with a name (must be an atom) and an age (must be a positive integer). Each field is validated independently and the results are combined with `validated::sequence/2`, which accumulates all errors:
| ?- Name = 123, Age = young,
validation::from_goal(atom(Name), Name, invalid_name, V1),
validation::from_goal((integer(Age), Age > 0), Age, invalid_age, V2),
validated::sequence([V1, V2], Result),
validation(Result)::or_else_throw(_).
uncaught exception: [invalid_name,invalid_age]
Both errors are reported. When all fields are valid, or_else_throw/1 returns the list of valid values:
| ?- Name = john, Age = 25,
validation::from_goal(atom(Name), Name, invalid_name, V1),
validation::from_goal((integer(Age), Age > 0), Age, invalid_age, V2),
validated::sequence([V1, V2], Result),
validation(Result)::or_else_throw(Values).
Values = [john,25]
yes
Compare with the expecteds library, where `either::sequence/2`
short-circuits at the first failure:
| ?- Name = 123, Age = young,
expected::from_goal(atom(Name), Name, invalid_name, E1),
expected::from_goal((integer(Age), Age > 0), Age, invalid_age, E2),
either::sequence([E1, E2], Result),
expected(Result)::or_else_throw(_).
uncaught exception: invalid_name
Only invalid_name is reported â `either::sequence/2` stops at the first
unexpected term.
The two libraries are complementary. Use expecteds for sequential
pipelines where only the first error matters. Use validations with
sequence/2, traverse/3, or zip/3 for independent validations where
all errors should be collected.
The expecteds and optionals libraries.