There's currently two constructors for custom contracts: from_predicate, which takes a boolean predicate and turn it into a contract, and custom, which takes a general partial identity.
The former is nice because it's simple to write (and will support boolean operations). However, it can't customize error messages, which is important for developer experience.
The latter is the most general, but it's not very natural to write, as we need to call blame and use label helpers to attach error data, and because we need to return the original value instead of just "ok" or "not ok".
This PR introduces a third form, in the middle, called validators. Validators leverage the enum variants of Nickel to cover the case of eager contracts (like predicates) but with all the customization power of custom. Basically, instead of returning a boolean, a validator returns [| 'Ok, 'Error <ErrorData> |] where ErrorData mimics the structure of a label, but as a normal Nickel record. Because it's eager, it's trivially convertible to a predicate, so validators will be usable with boolean operators.
The hope is that many concrete cases are actually validator, which are simpler to write than general custom contracts, and have much better properties (they can be negated, or-ed, etc.).
Content
This PR:
Adds a new custom contract variant in the AST, Validator
Adds the corresponding primop to build them, %contract/from_validator%, as well as the corresponding stdlib wrapper std.contract.from_validator
Update the examples and the stdlib to use validators whenever possible, instead of general custom contracts
Update the contracts section of the manual to introduce validators
Depends on #1964. Closes #1957.
Motivation
There's currently two constructors for custom contracts:
from_predicate
, which takes a boolean predicate and turn it into a contract, andcustom
, which takes a general partial identity.The former is nice because it's simple to write (and will support boolean operations). However, it can't customize error messages, which is important for developer experience.
The latter is the most general, but it's not very natural to write, as we need to call blame and use label helpers to attach error data, and because we need to return the original value instead of just "ok" or "not ok".
This PR introduces a third form, in the middle, called validators. Validators leverage the enum variants of Nickel to cover the case of eager contracts (like predicates) but with all the customization power of
custom
. Basically, instead of returning a boolean, a validator returns[| 'Ok, 'Error <ErrorData> |]
whereErrorData
mimics the structure of a label, but as a normal Nickel record. Because it's eager, it's trivially convertible to a predicate, so validators will be usable with boolean operators.The hope is that many concrete cases are actually validator, which are simpler to write than general custom contracts, and have much better properties (they can be negated, or-ed, etc.).
Content
This PR:
Validator
%contract/from_validator%
, as well as the corresponding stdlib wrapperstd.contract.from_validator