Replies: 3 comments 11 replies
|
The assertion that optional fields don't exist is hard to accept. This area of CUE is still hard to understand. My coworkers have asked me several times recently what it means for an optional field to have a concrete value, or to have a default value. My initial take is that You shared this example: a?: 123
x: isConcrete(a)I read that and think, " a?: 123
a: _ // Effectively no statement, other than that 'a' should be exported.
x: isConcrete(a)Now, you could say, "Well, that's a different question; you're asking about a different Again, I read the |
|
Does this provide a way to check for an optional field having a value, but raising an error if that field can never exist? There is the combination of |
|
Some feedback from a full-scale adoption exercise. helm2cue generates CUE from Helm charts, and its output leans heavily on the historical Overall: it works, and the output reads far better than the bottom comparisons. But having looked harder at why it works, my headline finding has changed: the drop-in only works because
1.
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Proposal: predicate builtins
Summary
Add a family of predeclared builtins that let CUE inspect a value's state — whether a field is present, whether an expression is valid, whether it is concrete, and whether a label is permitted — returning a boolean without propagating errors. They are first-class, well-defined replacements for the long-standing comparison-to-bottom idiom (
!= _|_/== _|_).The set:
exists(ref)allows(s, label)label(string field / int index) appear ons?isValid(expr)exprevaluate without a permanent error?isConcrete(expr)exprevaluate to a concrete value?existsN(n, refs…)nof the referenced fields exist?validN(n, exprs…)nof the expressions valid?concreteN(n, exprs…)nof the expressions concrete?In every
…Nvalidator,nis the same kind of count constraint: a literal requires exactly that many, and a bound such as>=2or<=1requires at least or at most that many.They are gated behind a new per-file experiment,
@experiment(predicates)(preview in v0.17.0). The normative semantics and worked examples are in the language spec (doc/ref/spec.md).This supersedes the relevant part of #943, with the other builtins it mentioned spun out separately (e.g.
mustis already tracked in #575).Motivation
Users routinely need to branch on properties that CUE's monotonic, error-propagating evaluation otherwise hides — is this field present? did this evaluate without error? is it concrete yet? Today this is written as a comparison to bottom:
That idiom is problematic:
Giving each question its own named builtin makes the intent explicit and the semantics specified, and lets the evaluator surface order-dependence as a cycle rather than a silent wrong answer.
A concrete driver is protobuf's
oneof— "exactly one of these fields is set." That is currently awkward to encode in CUE (it needs a disjunction of structs or hand-written constraints);existsN(1, a, b, c)expresses it directly. The count validators generalize this "N of M" pattern to presence (existsN), validity (validN), and concreteness (concreteN).Proposal
All builtins are predeclared and gated behind
@experiment(predicates).Functions (
→ bool)exists(ref)— whether the referenced field is present as a regular member.refmust be a reference (field reference, selector, or index expression); a non-reference argument is an error.allows(s, label)— whetherlabel(a string field name or integer index) may appear on the struct/lists. It is the structural "may ever appear" counterpart toexists, and the two compose:allows(s, "k") && exists(s.k).isValid(expr)—trueiffexprdoes not evaluate to a permanent error; an incomplete value that has no internal inconsistencies and could still be made concrete is valid.isConcrete(expr)— whetherexpris concrete (recursively for structs and lists; only regular fields are considered).Count validators
existsN,validN, andconcreteNare embedded in a struct to constrain how many of their arguments satisfy a property. All three share the same shape and count semantics: the first argument is the count — a literalnrequires exactlyn, while a bound such as>=2or<=1requires at least or at most that many — and the rest are references (existsN) or expressions (validN,concreteN).existsNdirectly encodes protobuf'soneofand its relatives:validNandconcreteNare the analogues for validity and concreteness:If a count constraint can never be satisfied, validation fails immediately; otherwise it stays incomplete until a definitive answer can be determined.
Open question
What does
isValidclose over?isValid(expr)is valid ifexprcan be made concrete, without error, by making it more specific. For a self-contained expression that is unambiguous. But when the argument is a reference, the value it ultimately takes can depend on parts of the configuration outside theisValidcall:aresolves throughc, which a siblingc: 5could still make concrete. The two readings give different results:isValid(a)istrue(a siblingc: 5can still makeaconcrete);ais judged only as it resolves within the call, wherecis an unsupplied optional,isValid(a)isfalse.This needs to be decided and written into the
isValiddefinition; the same question applies tovalidN.What does
isConcretereport for an optional or required field?A referenced field may be optional or required rather than a present regular member:
The value
123is concrete, yet a is not present.Two readings are possible:
isConcrete(a)is false (pragmatically, a field that is not present is not concrete), or it is an incomplete error (it is unclear what "concrete" should even mean for a field that does not yet exist). Either way, and unlike exists, which simply answers false for an absent field,isConcretearguably requires the field to exist before its concreteness is a meaningful question. Also, if a regular field with the value referred to byisConcretehas an incomplete error, that is an error. The same should hold for the top level. At least these two cases should be consistent. The same applies toconcreteN.Relationship to #943
#943 is stale (it predates
allowsandisValid), too broad (it also coversmust,range, …), and has accumulated unrelated discussion. This proposal pins down the concrete, implemented design for the presence/validity/concreteness predicates, and deliberately uses the namepredicatesrather than "reflect"/"introspection" — reserving the latter for possible future type or schema reflection.All reactions