Class ConstraintValueSearch
Why probing, when ConstraintWindows already reads the constraint? The fold is exact
but narrow: Integer bases, and-combinations, bound operators only. Everything else — a Float
range, a Date window, an or, a composite — folds to nothing, and the generator falls
back to spraying the base type's general literal pool and letting E08260 prune. That works only while
the pool HAPPENS to contain an admissible value. When it does not, the run produces no set value at
all, and producing nothing is itself the signal: every candidate sat outside the constraint,
so the thing to do is go and look for where the admissible values actually are.
The oracle makes this cheap and safe. CompileTimeConstraintEvaluator is a reflective
interpreter over the real EK9 standard library — it builds subject and literal with the same
_of(String) factory the bytecode generator emits, so its verdict is byte-for-byte faithful to
runtime for ANY constrainable base type, with no per-type special-casing. Two consequences shape this
class:
- A probe costs a reflective call, not a compile pass — so we can afford hundreds, and we only spend them on the failure path.
- Every value returned here has been verified PASS before it is returned. The search can therefore never widen the population into values that will be pruned. Too narrow costs coverage; too wide is impossible by construction — the same one-sided posture the rest of the generator takes.
Where the seeds come from — the constraint names them. A blind numeric hunt cannot find a
narrow window far from the origin, which is why bisection alone is not enough to start. But the
constraint's own literals ARE the interesting coordinates: >= 1000.0 and <= 2000.0 carries
1000.0 and 2000.0. Probing those, their neighbours (for a strict bound like > 5, where the
literal itself fails and 6 succeeds) and their pairwise midpoints (for a strict bound on a continuous
base, where > 0.0 and < 1.0 fails at both ends and passes at 0.5) yields a verified seed for
essentially any bound-based constraint — on Integer, Float, Date, Money or anything else, because the
seeds are literal TEXT handed to a type-agnostic evaluator.
Only once a seed is verified does bisection earn its place: from a value known to be inside, widen outward to the true edges. That is sound where a blind bisection is not, because it starts from a point in the admissible set rather than hoping to land in one.
🔑 A suffixed value varies along TWO axes, and only one of them may move. Money is
an amount AND a currency; Dimension is a magnitude AND a unit. Comparing across the suffix is
not decidable at compile time — 10cm against >= 500mm evaluates UNKNOWN, not FAIL,
even though 10cm is plainly below 500mm — so a candidate carrying a different suffix from the
constraint escapes the E08260 filter and then Panics at runtime. Everything generated here therefore
pins the suffix to the one the constraint itself declared and moves only the numeric part. The
type's own ++ / -- already behave this way (Dimension._inc re-attaches its
suffix, Money._inc adds one minor unit of its own currency), which is why stepping is
preferred over arithmetic here.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionadmissibleValues(String baseTypeFqn, ConstraintExpr constraint) Values ofbaseTypeFqnthat provably satisfyconstraint, most useful first, or an empty list when probing found none.
-
Constructor Details
-
ConstraintValueSearch
public ConstraintValueSearch()
-
-
Method Details
-
admissibleValues
Values ofbaseTypeFqnthat provably satisfyconstraint, most useful first, or an empty list when probing found none.An empty result does NOT prove the type is uninhabitable — only that this bounded search did not find a witness. The two are different claims and must not be conflated.
- Parameters:
baseTypeFqn- the constrained base type's fully qualified nameconstraint- the constraint to satisfy- Returns:
- verified-admissible literal texts
-