🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsAnnouncing new builds for 11 September 2026(11.09.2026 um 19:11 Uhr)
🪟 Windows TippsChild account not showing in Microsoft Family(11.09.2026 um 11:11 Uhr)
🪟 Windows TippsUnable to connect to localhost MySQL workbench(11.09.2026 um 14:07 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)
🪟 Windows TippsThe Gemini desktop app is now available for Windows(11.09.2026 um 17:06 Uhr)
🪟 Windows TippsAnnouncing new builds for 11 September 2026(11.09.2026 um 19:11 Uhr)
🪟 Windows TippsChild account not showing in Microsoft Family(11.09.2026 um 11:11 Uhr)
🪟 Windows TippsUnable to connect to localhost MySQL workbench(11.09.2026 um 14:07 Uhr)
⚠️ Malware / Trojaner / VirenWindows 11 just dropped the tool ransomware abused, Microsoft says don’t restore WMIC(10.09.2026 um 20:11 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 6 Min Lesezeit
0

My reasoning engine proved that 114 is prime — a debugging story about negation

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

CODE
zelph> 114 testprime 114
( 114 isprime 114 ) ⇐ {(¬( 114 hasdivisor D)) ( 114 testprime 114 )}






114 is 2 · 3 · 19. My engine was very confident it's prime. Even better: it printed a proof.



Quick context: I build ). As a showcase, I wanted a primality test written the way a textbook states it:




N is prime if no candidate divides it.




In zelph syntax:




CODE
(N testprime N, ¬(N hasdivisor D)) => (N isprime N)






¬ is negation-as-failure: the condition succeeds if no matching fact exists. Other rules enumerate divisor candidates and assert (N mod D) facts, which the division module answers. The rule looks correct. It even is correct — as a formula. As a program, it was broken twice.






Bug #1: 114 wasn't even a number



In zelph, 114 without a prefix parses as an atom — a node whose name is "114" — not as a digit list. No arithmetic rule can divide an atom, so no hasdivisor fact could ever exist, so the negation was trivially true, so everything was prime. The classic "your test tests nothing" bug, in logic form. Fix: number literals are written &114. Moving on — because bug #2 is the one worth a blog post.






Bug #2: a race condition in pure logic



Even with real numbers, the rule kept deriving isprime for composite numbers — sometimes, depending on the order in which rules had been defined. When a bug depends on ordering, I stop debugging the big system and distill. This is the entire bug class in four lines:




CODE
(A trigger A) => (A step1 A)
(A step1 A) => (A step2 A)
(A trigger A, ¬(A step2 A)) => (A racewin A)
x trigger x






step2 is always derivable from trigger. So racewin should never fire. The old engine:




CODE
( x   step1   x )  ⇐ ( x   trigger   x )
( x racewin x ) ⇐ {(¬( x step2 x )) ( x trigger x )}
( x step2 x ) ⇐ ( x step1 x )






There it is, in the output order: the negation was evaluated while the answer was still being computed. At that moment x step2 x didn't exist yet, so ¬(...) succeeded, so racewin was derived. And here's the trap that makes this fatal rather than transient:



A forward chainer is monotonic. Facts are only ever added, never retracted. When step2 arrived one inference step later, it was too late — the wrong conclusion was already a fact, with a proof attached, and nothing in the engine's universe could take it back.



For the primality test this meant: isprime(42) raced against dozens of mod computations cascading through many fixpoint iterations. Whoever finished first won.






The scariest part: sometimes it was right



A variation of the probe — same shape, one extra indirection — produced the correct result on my machine. Why? Because the engine iterates its rules from an unordered hash set, and the hash order happened to schedule the fact-producing rule before the negation rule. I've debugged race conditions between threads before. A race condition inside single-threaded logic, decided by unordered_set iteration order, was new to me.






The fix: stratification



The theory has been known since the 1980s (stratified Datalog). My own documentation even claimed zelph had these semantics. The engine just... didn't implement them. Ouch. The fix:




  • Rules whose conditions contain a ¬ (at any nesting depth) form a deferred stratum.

  • Phase 1: run all purely positive rules to their fixpoint.

  • Phase 2: only then evaluate the deferred rules — against the saturated fact base.

  • Their consequences may enable positive rules again, so the phases alternate until neither derives anything.



The soundness argument is one sentence, and I find it satisfying that the villain of this story is also the hero: monotonicity caused the bug (no retraction) and also fixes it — new facts can make a negation fail, but never make it newly succeed. So a negation that succeeds after positive quiescence is final. The four-line repro and its friends are now permanent regression tests in the suite.






The payoff






CODE
zelph> .import arithmetic
zelph> .import primes-naf
zelph> (&113 testprime &113) = X
((&113 testprime &113) = prime) ⇐ {(&113 isprime &113) ...}
zelph> (&42 testprime &42) = X
((&42 testprime &42) = composite) ⇐ {(&42 hasdivisor &2) ...}






The textbook rule, executable as written — with the entire arithmetic underneath (comparison, subtraction, multiplication, Euclidean division with remainder) running as forward-chaining graph rewriting, arbitrary precision included.



One more thing I find mildly beautiful: the standard library also ships a negation-free twin (primes.zph). It proves primality via a positive fold — "no divisor up to D" grows one verified candidate at a time — and the fold doubles as a scheduler: for composite N, the search halts at the smallest divisor. On composites it's roughly 3–4× faster than the eager NAF version; on primes they tie, because both must pay for the full scan up to √N. Same mathematics, two proof strategies — and the proof strategy is the execution strategy.






Takeaways




  1. Negation-as-failure in a forward chainer is a scheduling problem, not just a semantics footnote. If your engine evaluates ¬ against in-flight state, you have a race — you just haven't lost it yet.

  2. When a bug depends on iteration order, don't debug the application. Distill it below five lines first; the repro is worth more than the fix, because it becomes the regression test.

  3. Documentation that describes semantics your code doesn't enforce is a bug report you wrote to yourself in advance.



Release notes for v0.9.8: https://github.com/acrion/zelph/releases/tag/v0.9.8

Try it: brew tap acrion/zelph && brew install zelph · choco install zelph · AUR: zelph



A question for people who've built or used forward-chaining systems (Datalog engines, rule engines, CEP systems): how does yours schedule negation? Static stratification analysis at compile time, runtime deferral like this, well-founded semantics, something else entirely? I chose runtime deferral because zelph's rules quantify over predicates (predicates are graph nodes), which makes static predicate-level stratification too coarse — but I'd genuinely like to hear other approaches.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
2 Quellen
The Gemini desktop app is now available for Windows
1 Quelle
Seamlessly import your team and data from Microsoft to Google Workspace during setup
1 Quelle
Announcing new builds for 11 September 2026
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten My reasoning engine proved that 114 is prime — a debugging story about negation

Thematisch verwandte Begriffe: reasoning, engine, proved, that · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...