eyeling 1.12.3 → 1.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/HANDBOOK.md CHANGED
@@ -124,7 +124,7 @@ If you want to follow the code in the same order Eyeling “thinks”, read:
124
124
  - deterministic Skolem IDs (head existentials + `log:skolem`) (`lib/skolem.js`)
125
125
  6. `lib/builtins.js` — builtin predicate evaluation plus shared literal/number/string/list helpers:
126
126
  - `makeBuiltins(deps)` dependency-injects engine hooks (unification, proving, deref, …)
127
- - exports `evalBuiltin(...)` and `isBuiltinPred(...)` back to the engine
127
+ - and returns `{ evalBuiltin, isBuiltinPred }` back to the engine
128
128
  - includes `materializeRdfLists(...)`, a small pre-pass that rewrites _anonymous_ `rdf:first`/`rdf:rest` linked lists into concrete N3 list terms so `list:*` builtins can work uniformly
129
129
  7. `lib/explain.js` — proof comments + `log:outputString` aggregation (fact ordering and pretty output).
130
130
  8. `lib/deref.js` — synchronous dereferencing for `log:content` / `log:semantics` (used by builtins and engine).
@@ -504,10 +504,10 @@ At each step:
504
504
  - apply the current substitution to it
505
505
  - attempt to satisfy it in three ways:
506
506
  1. built-ins
507
- 2. facts
508
- 3. backward rules
507
+ 2. backward rules
508
+ 3. facts
509
509
 
510
- Eyeling’s order is intentional: built-ins often bind variables cheaply; rules expand search trees.
510
+ Eyeling’s order is intentional: built-ins often bind variables cheaply; backward rules expand the search tree (and enable recursion); facts are tried last as cheap terminal matches.
511
511
 
512
512
  ### 8.3 Built-ins: return _deltas_, not full substitutions
513
513
 
@@ -539,9 +539,15 @@ Conjunction in N3 is order-insensitive, but many builtins are only useful once s
539
539
 
540
540
  That second case matters for “satisfiable but non-enumerating” builtins (e.g., some `log:` helpers) where early vacuous success would otherwise prevent later goals from ever binding the variables the builtin needs.
541
541
 
542
- ### 8.4 Loop prevention: a simple visited list
542
+ ### 8.4 Loop prevention: visited multiset with backtracking
543
543
 
544
- Eyeling prevents obvious infinite recursion by skipping a goal if it is already in the `visited` list. This is a pragmatic check; it doesn’t implement full tabling, but it avoids the most common “A depends on A” loops.
544
+ Eyeling avoids obvious infinite recursion by recording each (substituted) goal it is currently trying in a per-branch _visited_ structure. If the same goal is encountered again on the same proof branch, Eyeling skips it.
545
+
546
+ Implementation notes:
547
+
548
+ - The visited structure is a `Map` from _goal key_ to a reference count, plus a trail array. This makes it cheap to check (`O(1)` average) and cheap to roll back on backtracking (just like the substitution trail).
549
+ - Keys are _structural_. Atoms use stable IDs; lists use element keys; variables use their identity (so two different variables are **not** conflated). This keeps the cycle check conservative and avoids accidental pruning.
550
+ - This is not full tabling: it does not memoize answers, it only guards against immediate cycles (the common “A depends on A” loops).
545
551
 
546
552
  ### 8.5 Backward rules: indexed by head predicate
547
553
 
@@ -566,7 +572,7 @@ To reduce allocation pressure, Eyeling reuses a single fresh `Var(...)` object p
566
572
  The trail-based substitution store removes the biggest accidental quadratic cost (copying a growing substitution object at every step).
567
573
  In deep and branchy searches, the substitution trail still grows, and long variable-to-variable chains increase the work done by `applySubstTerm`.
568
574
 
569
- Eyeling currently keeps the full trail as-is during search and when emitting answers; it does not run a substitution compaction pass, and it does not perform explicit substitution composition.
575
+ Eyeling currently keeps the full trail as-is during search. When emitting a solution, it runs a lightweight compaction pass (via `gcCollectVarsInGoals(...)` / `gcCompactForGoals(...)`) so only bindings reachable from the answer variables and remaining goals are kept. It still does not perform general substitution composition/normalization during search.
570
576
 
571
577
  ---
572
578
 
@@ -1923,13 +1929,13 @@ Logic & reasoning background (Wikipedia):
1923
1929
 
1924
1930
  RDF succeeded by making a radical constraint feel natural: reduce meaning to small, uniform statements—triples—that can be published, merged, and queried across boundaries. A triple does not presume a database schema, a programming language, or a particular application. It presumes only that names (IRIs) can be shared, and that graphs can be combined.
1925
1931
 
1926
- That strength also marks RDF’s limit. The moment a graph is expected to *do* something—normalize values, reconcile vocabularies, derive implied relationships, enforce a policy, compute a small transformation—logic tends to migrate into code. The graph becomes an inert substrate while the decisive semantics hide in scripts, services, ETL pipelines, or bespoke rule engines. What remains portable is the data; what often becomes non-portable is the meaning.
1932
+ That strength also marks RDF’s limit. The moment a graph is expected to _do_ something—normalize values, reconcile vocabularies, derive implied relationships, enforce a policy, compute a small transformation—logic tends to migrate into code. The graph becomes an inert substrate while the decisive semantics hide in scripts, services, ETL pipelines, or bespoke rule engines. What remains portable is the data; what often becomes non-portable is the meaning.
1927
1933
 
1928
- Notation 3 (N3) sits precisely at that seam. It remains a readable way to write RDF, but it also treats *graphs themselves* as objects that can be described, matched, and related. The N3 Community Group’s specification presents N3 as an assertion and logic language that extends RDF rather than replacing it: [https://w3c.github.io/N3/spec/](https://w3c.github.io/N3/spec/).
1934
+ Notation 3 (N3) sits precisely at that seam. It remains a readable way to write RDF, but it also treats _graphs themselves_ as objects that can be described, matched, and related. The N3 Community Group’s specification presents N3 as an assertion and logic language that extends RDF rather than replacing it: [https://w3c.github.io/N3/spec/](https://w3c.github.io/N3/spec/).
1929
1935
 
1930
1936
  The essential move is quotation: writing a graph inside braces as a thing that can be discussed. Once graphs can be quoted, rules become graph-to-graph transformations. The familiar implication form, `{ … } => { … } .`, reads as a piece of prose: whenever the antecedent pattern holds, the consequent pattern follows. Tim Berners-Lee’s design note frames this as a web-friendly logic with variables and nested graphs: [https://www.w3.org/DesignIssues/Notation3.html](https://www.w3.org/DesignIssues/Notation3.html).
1931
1937
 
1932
- This style of rule-writing is more than syntactic sugar. It keeps the unit of exchange stable. Inputs are RDF graphs; outputs are RDF graphs. Inference produces new triples rather than hidden internal state. Rule sets can be versioned alongside data, reviewed as text, and executed by different engines that implement the same semantics. That portability theme runs back to the original W3C Team Submission: [https://www.w3.org/TeamSubmission/n3/](https://www.w3.org/TeamSubmission/n3/).
1938
+ This style of rule-writing makes rules first-class, publishable artifacts. It keeps the unit of exchange stable. Inputs are RDF graphs; outputs are RDF graphs. Inference produces new triples rather than hidden internal state. Rule sets can be versioned alongside data, reviewed as text, and executed by different engines that implement the same semantics. That portability theme runs back to the original W3C Team Submission: [https://www.w3.org/TeamSubmission/n3/](https://www.w3.org/TeamSubmission/n3/).
1933
1939
 
1934
1940
  Practical reasoning also depends on computation: lists, strings, math, comparisons, and the other “small operations” that integration work demands. N3 addresses this by standardizing built-ins—predicates with predefined behavior that can be used inside rule bodies while preserving the declarative, graph-shaped idiom. The built-ins report is here: [https://w3c.github.io/N3/reports/20230703/builtins.html](https://w3c.github.io/N3/reports/20230703/builtins.html).
1935
1941
 
@@ -1941,4 +1947,4 @@ The comparison with older tools is historically instructive. Cwm (Closed World M
1941
1947
 
1942
1948
  What motivates Notation 3, in the end, is architectural restraint. It refuses to let “logic” become merely a private feature of an application stack. It keeps meaning close to the graph: rules are expressed as graph patterns; results are expressed as triples; computation is pulled in through well-defined built-ins rather than arbitrary code. This produces a style of working where integration and inference are not sidecar scripts, but publishable artifacts—documents that can be inspected, shared, tested, and reused.
1943
1949
 
1944
- In that sense, N3 is less a bid to make the web “smarter” than a bid to make meaning *portable*: not only facts that travel, but also the explicit steps by which facts can be connected, extended, and made actionable—without abandoning the simplicity that made triples travel in the first place.
1950
+ In that sense, N3 is less a bid to make the web “smarter” than a bid to make meaning _portable_: not only facts that travel, but also the explicit steps by which facts can be connected, extended, and made actionable—without abandoning the simplicity that made triples travel in the first place.
@@ -0,0 +1,89 @@
1
+ # ==================
2
+ # Ackermann function
3
+ # ==================
4
+ #
5
+ # See https://en.wikipedia.org/wiki/Ackermann_function
6
+
7
+ @prefix math: <http://www.w3.org/2000/10/swap/math#>.
8
+ @prefix log: <http://www.w3.org/2000/10/swap/log#>.
9
+ @prefix : <https://eyereasoner.github.io/ns#>.
10
+
11
+ # ackermann(x, y)
12
+ {
13
+ (?X ?Y) :ackermann ?A.
14
+ } <= {
15
+ (?Y 3) math:sum ?B.
16
+ (?X ?B 2) :ackermann ?C.
17
+ (?C 3) math:difference ?A.
18
+ }.
19
+
20
+ # ackermann(x, y, z)
21
+ # succ (x=0)
22
+ {
23
+ (0 ?Y ?Z) :ackermann ?A.
24
+ } <= {
25
+ (?Y 1) math:sum ?A.
26
+ }.
27
+
28
+ # sum (x=1)
29
+ {
30
+ (1 ?Y ?Z) :ackermann ?A.
31
+ } <= {
32
+ (?Y ?Z) math:sum ?A.
33
+ }.
34
+
35
+ # product (x=2)
36
+ {
37
+ (2 ?Y ?Z) :ackermann ?A.
38
+ } <= {
39
+ (?Y ?Z) math:product ?A.
40
+ }.
41
+
42
+ # exponentiation (x=3), tetration (x=4), pentation (x=5), hexation (x=6), etc
43
+ {
44
+ (?X 0 ?Z) :ackermann 1.
45
+ } <= {
46
+ ?X math:greaterThan 2.
47
+ }.
48
+
49
+ {
50
+ (?X ?Y ?Z) :ackermann ?A.
51
+ } <= {
52
+ ?X math:greaterThan 2.
53
+ ?Y math:notEqualTo 0.
54
+ (?Y 1) math:difference ?B.
55
+ (?X ?B ?Z) :ackermann ?C.
56
+ (?X 1) math:difference ?D.
57
+ (?D ?C ?Z) :ackermann ?A.
58
+ }.
59
+
60
+ # test
61
+ {
62
+ (0 0) :ackermann ?A0.
63
+ (0 6) :ackermann ?A1.
64
+ (1 2) :ackermann ?A2.
65
+ (1 7) :ackermann ?A3.
66
+ (2 2) :ackermann ?A4.
67
+ (2 9) :ackermann ?A5.
68
+ (3 4) :ackermann ?A6.
69
+ (3 1000) :ackermann ?A7.
70
+ (4 0) :ackermann ?A8.
71
+ (4 1) :ackermann ?A9.
72
+ #(4 2) :ackermann ?A10.
73
+ (5 0) :ackermann ?A11.
74
+ } => {
75
+ :test :is {
76
+ (0 0) :ackermann ?A0.
77
+ (0 6) :ackermann ?A1.
78
+ (1 2) :ackermann ?A2.
79
+ (1 7) :ackermann ?A3.
80
+ (2 2) :ackermann ?A4.
81
+ (2 9) :ackermann ?A5.
82
+ (3 4) :ackermann ?A6.
83
+ (3 1000) :ackermann ?A7.
84
+ (4 0) :ackermann ?A8.
85
+ (4 1) :ackermann ?A9.
86
+ #(4 2) :ackermann ?A10.
87
+ (5 0) :ackermann ?A11.
88
+ }.
89
+ }.
@@ -22,12 +22,14 @@
22
22
  1 :fibonacci ?F1.
23
23
  10 :fibonacci ?F10.
24
24
  100 :fibonacci ?F100.
25
+ 1000 :fibonacci ?F1000.
25
26
  } => {
26
27
  :test :is {
27
28
  0 :fibonacci ?F0.
28
29
  1 :fibonacci ?F1.
29
30
  10 :fibonacci ?F10.
30
31
  100 :fibonacci ?F100.
32
+ 1000 :fibonacci ?F1000.
31
33
  }.
32
34
  }.
33
35
 
@@ -0,0 +1,15 @@
1
+ @prefix : <https://eyereasoner.github.io/ns#> .
2
+
3
+ :test :is {
4
+ (0 0) :ackermann 1 .
5
+ (0 6) :ackermann 7 .
6
+ (1 2) :ackermann 4 .
7
+ (1 7) :ackermann 9 .
8
+ (2 2) :ackermann 7 .
9
+ (2 9) :ackermann 21 .
10
+ (3 4) :ackermann 125 .
11
+ (3 1000) :ackermann 85720688574901385675874003924800144844912384936442688595500031069628084089994889799455870305255668650207573833404251746014971622855385123487876620597588598431476542198593847883368596840498969135023633457224371799868655530139190140473324351568616503316569571821492337341283438653220995094697645344555005 .
12
+ (4 0) :ackermann 13 .
13
+ (4 1) :ackermann 65533 .
14
+ (5 0) :ackermann 65533 .
15
+ } .
@@ -5,4 +5,5 @@
5
5
  1 :fibonacci 1 .
6
6
  10 :fibonacci 55 .
7
7
  100 :fibonacci 354224848179261915075 .
8
+ 1000 :fibonacci 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875 .
8
9
  } .