pub fn enumerate_tuples<F>(
canonical: &Arc<PolydatKernel>,
parent: &Arc<PolydatKernel>,
clauses: &[Clause],
filter: Option<&str>,
on_empty_clause: F,
) -> Result<Vec<Vec<(String, Value)>>, String>Expand description
Enumerate the typed tuples a Cartesian comprehension produces.
Walks the dependent-tuple tree depth-first using fresh
per-branch kernels. Each branch installs the prior clauses’
typed values as inputs on a fresh subscope kernel
(PolydatKernel::materialize_subscope),
then evaluates the next clause’s spec against that kernel.
This is the kernel-per-logical-subspace rule from SRD-18b
§“Dependent Tuple Iteration”.
filter, when provided, is evaluated against each fully-bound
tuple — the predicate text is interpolated against a kernel
with all clause values installed, then eval_const_expr_for
(charged to the scope’s ledger) runs
it to a Value::Bool. Tuples where the predicate is false
are skipped. Predicate evaluation errors (non-Bool result,
unresolved name, etc.) abort enumeration. See
Comprehension::filter.
Empty-clause handling is delegated to on_empty_clause: the
caller decides whether to propagate as a hard error (strict
mode) or warn-and-skip (relaxed mode). The callback receives
the offending Clause (which carries both single-var and
parallel-iter shapes) and returns Result<(), String> —
returning Err aborts enumeration, Ok(()) skips the
branch.