datalog: anonymous _ in negation is existential, not unbound
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 23s
The canonical Datalog idiom for "no X has any Y": orphan(X) :- person(X), not(parent(X, _)). was rejected by the safety check with "negation refers to unbound variable(s) (\"_anon1\")". The parser renames each anonymous `_` to a fresh `_anon*` symbol so multiple `_` occurrences don't unify with each other, and the negation safety walk then demanded all free vars in the negated lit be bound by an earlier positive body lit — including the renamed anonymous vars. Anonymous vars in a negation are existentially quantified within the negation, not requirements from outside. Added dl-non-anon-vars to strip `_anon*` names from the `needed` set before the binding check in dl-process-neg!. Real vars (like `X` in the orphan idiom) still must be bound by an earlier positive body lit, just as before. 2 new regression tests (orphan idiom + multi-anon "solo" pattern); conformance 273/273. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ for rose-ash data (e.g. federation graph, content relationships).
|
||||
|
||||
## Status (rolling)
|
||||
|
||||
`bash lib/datalog/conformance.sh` → **271/271 across 11 suites**
|
||||
`bash lib/datalog/conformance.sh` → **273/273 across 11 suites**
|
||||
(tokenize, parse, unify, eval, builtins, semi_naive, negation, aggregates,
|
||||
api, magic, demo). Source is ~3100 LOC, tests ~2900 LOC, public API
|
||||
documented in `lib/datalog/datalog.sx`.
|
||||
@@ -320,6 +320,18 @@ large graphs.
|
||||
|
||||
_Newest first._
|
||||
|
||||
- 2026-05-11 — Anonymous `_` in a negated literal was incorrectly
|
||||
flagged by the safety check. The canonical idiom
|
||||
`orphan(X) :- person(X), not(parent(X, _))` was rejected with
|
||||
"negation refers to unbound variable(s) (\"_anon1\")" because the
|
||||
parser renames each `_` to a fresh `_anon*` symbol and the negation
|
||||
safety walk demanded all vars in the negated lit be bound by an
|
||||
earlier positive body literal. Anonymous vars in negation are
|
||||
existentially quantified — they shouldn't need outer binding.
|
||||
Added `dl-non-anon-vars` filter; `dl-process-neg!` now strips
|
||||
`_anon*` names from `needed` before the binding check. 2 new
|
||||
regression tests; 273/273.
|
||||
|
||||
- 2026-05-11 — Compound terms in fact-arg / rule-head positions were
|
||||
silently stored as unreduced expressions. `p(+(1, 2)).` resulted
|
||||
in a tuple `(p (+ 1 2))` (dl-ground? sees no free variables, so it
|
||||
|
||||
Reference in New Issue
Block a user