gen.optional: absent-or-value without colliding with null #2
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Missing
gen.optional g(orgen.maybe) producing "absent" or a value, with a representation that does not collide withnullas a legitimate value.Where it was needed
jsonschema2nix draws objects where each property may be unset, set to
null(nullable schemas) or set to a value. Two hand-rolled encodings coexist intests/generators.nixbecause there is no primitive:The first cannot express a present
null, so it wraps every value in{ value = ...; }; the second threadshasthrough every consumer. Both obscure the intent and neither shrinks "present -> absent" as the natural first step.Suggested shape
gen.optional g : Gen { has : bool; value : a }orGen (null | { value }), shrinking toward absent first.gen.recordWithOptional { k = g; ... }or agen.recordvariant that drops absent fields, sofilterAttrsdisappears from callers.Plan
Representation:
null | { value = v; }. Nix has noMaybe; a{ has; value }tag has to be threaded through every consumer, whereasif o == null then … else o.valueis the shortest pattern match and does not collide with a presentnull.Generation as Hedgehog
Gen.maybe:Absent is common at small sizes and, because
frequencyshrinks toward the first alternative, "present -> absent" is the first shrink step.Records are the main consumer, so add
gen.recordWith { required = { … }; optional = { … }; }after Hypothesisfixed_dictionaries(mapping, optional=…)/ fast-checkfc.record(model, { requiredKeys }): absent fields are dropped, sofilterAttrsdisappears from callers and most of them never see the wrapper.Depends on #6 (
frequency) and #5 (sized).Implemented in
456e65c, pushed to main.