eyeling 1.12.3 → 1.12.5
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 +37 -11
- package/examples/ackermann.n3 +89 -0
- package/examples/fibonacci.n3 +2 -0
- package/examples/output/ackermann.n3 +15 -0
- package/examples/output/fibonacci.n3 +1 -0
- package/examples/output/patch.n3 +10 -0
- package/examples/patch.n3 +55 -0
- package/eyeling.js +358 -139
- package/lib/builtins.js +41 -33
- package/lib/engine.js +290 -98
- package/lib/parser.js +0 -2
- package/lib/prelude.js +27 -4
- package/lib/rules.js +0 -2
- package/package.json +1 -1
package/HANDBOOK.md
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
- [Epilogue](#epilogue)
|
|
28
28
|
- [Appendix A — Eyeling user notes](#app-a)
|
|
29
29
|
- [Appendix B — Notation 3: when facts can carry their own logic](#app-b)
|
|
30
|
+
- [Appendix C — N3 beyond Prolog: logic that survives the open web](#app-c)
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
@@ -124,7 +125,7 @@ If you want to follow the code in the same order Eyeling “thinks”, read:
|
|
|
124
125
|
- deterministic Skolem IDs (head existentials + `log:skolem`) (`lib/skolem.js`)
|
|
125
126
|
6. `lib/builtins.js` — builtin predicate evaluation plus shared literal/number/string/list helpers:
|
|
126
127
|
- `makeBuiltins(deps)` dependency-injects engine hooks (unification, proving, deref, …)
|
|
127
|
-
-
|
|
128
|
+
- and returns `{ evalBuiltin, isBuiltinPred }` back to the engine
|
|
128
129
|
- 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
130
|
7. `lib/explain.js` — proof comments + `log:outputString` aggregation (fact ordering and pretty output).
|
|
130
131
|
8. `lib/deref.js` — synchronous dereferencing for `log:content` / `log:semantics` (used by builtins and engine).
|
|
@@ -504,10 +505,10 @@ At each step:
|
|
|
504
505
|
- apply the current substitution to it
|
|
505
506
|
- attempt to satisfy it in three ways:
|
|
506
507
|
1. built-ins
|
|
507
|
-
2.
|
|
508
|
-
3.
|
|
508
|
+
2. backward rules
|
|
509
|
+
3. facts
|
|
509
510
|
|
|
510
|
-
Eyeling’s order is intentional: built-ins often bind variables cheaply; rules expand search
|
|
511
|
+
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
512
|
|
|
512
513
|
### 8.3 Built-ins: return _deltas_, not full substitutions
|
|
513
514
|
|
|
@@ -539,9 +540,15 @@ Conjunction in N3 is order-insensitive, but many builtins are only useful once s
|
|
|
539
540
|
|
|
540
541
|
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
542
|
|
|
542
|
-
### 8.4 Loop prevention:
|
|
543
|
+
### 8.4 Loop prevention: visited multiset with backtracking
|
|
543
544
|
|
|
544
|
-
Eyeling
|
|
545
|
+
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.
|
|
546
|
+
|
|
547
|
+
Implementation notes:
|
|
548
|
+
|
|
549
|
+
- 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).
|
|
550
|
+
- 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.
|
|
551
|
+
- This is not full tabling: it does not memoize answers, it only guards against immediate cycles (the common “A depends on A” loops).
|
|
545
552
|
|
|
546
553
|
### 8.5 Backward rules: indexed by head predicate
|
|
547
554
|
|
|
@@ -566,7 +573,7 @@ To reduce allocation pressure, Eyeling reuses a single fresh `Var(...)` object p
|
|
|
566
573
|
The trail-based substitution store removes the biggest accidental quadratic cost (copying a growing substitution object at every step).
|
|
567
574
|
In deep and branchy searches, the substitution trail still grows, and long variable-to-variable chains increase the work done by `applySubstTerm`.
|
|
568
575
|
|
|
569
|
-
Eyeling currently keeps the full trail as-is during search
|
|
576
|
+
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
577
|
|
|
571
578
|
---
|
|
572
579
|
|
|
@@ -1923,13 +1930,13 @@ Logic & reasoning background (Wikipedia):
|
|
|
1923
1930
|
|
|
1924
1931
|
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
1932
|
|
|
1926
|
-
That strength also marks RDF’s limit. The moment a graph is expected to
|
|
1933
|
+
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
1934
|
|
|
1928
|
-
Notation 3 (N3) sits precisely at that seam. It remains a readable way to write RDF, but it also treats
|
|
1935
|
+
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
1936
|
|
|
1930
1937
|
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
1938
|
|
|
1932
|
-
This style of rule-writing
|
|
1939
|
+
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
1940
|
|
|
1934
1941
|
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
1942
|
|
|
@@ -1941,4 +1948,23 @@ The comparison with older tools is historically instructive. Cwm (Closed World M
|
|
|
1941
1948
|
|
|
1942
1949
|
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
1950
|
|
|
1944
|
-
In that sense, N3 is less a bid to make the web “smarter” than a bid to make meaning
|
|
1951
|
+
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.
|
|
1952
|
+
|
|
1953
|
+
---
|
|
1954
|
+
|
|
1955
|
+
## Appendix C — N3 beyond Prolog: logic that survives the open web
|
|
1956
|
+
|
|
1957
|
+
At first glance, an N3 rule set can feel familiar if you’ve used Prolog: variables, unification, and rules that read like “if this pattern holds, then that pattern follows.” But N3 is not just “logic programming with a different syntax.” It is logic shaped for a different environment: not a single program with a single database, but a world of distributed graphs that can be published, merged, and cited across boundaries.
|
|
1958
|
+
|
|
1959
|
+
That change of environment forces a change in what “beyond Prolog” even means. It is less about being more powerful in the abstract, and more about being *more portable as meaning* — logic that stays connected when it moves between documents, vocabularies, and authors.
|
|
1960
|
+
|
|
1961
|
+
Several design moves push N3 into that web-native space:
|
|
1962
|
+
|
|
1963
|
+
- **Global identity is the default.** Names are IRIs. A rule does not merely compute with local symbols; it operates over identifiers meant to be shared across datasets.
|
|
1964
|
+
- **Graphs are the unit of exchange.** The input is a graph; the output is a graph. Inference produces new triples rather than hidden internal state, so results can travel the same way the facts do.
|
|
1965
|
+
- **Statements can be treated as data.** Quoted graphs let you talk *about* assertions: claims, policies, provenance, “this source says …,” “this formula implies …,” and other meta-level structure that is awkward in a plain predicate database.
|
|
1966
|
+
- **Rules can be publishable artifacts.** Rules can live alongside data as text, be versioned, reviewed, and reused — the “meaning” is not forced back into an external codebase.
|
|
1967
|
+
- **Web-like computation can be pulled into rule bodies.** Built-ins make room for the small computations that real integration needs (strings, lists, comparisons), and some N3 workflows even treat IRIs as pointers to more knowledge.
|
|
1968
|
+
|
|
1969
|
+
In that sense, Prolog is a superb engine for proving things *inside* a chosen world. N3 is a way to write rules so they keep working *across* worlds: across documents, across graph boundaries, and across the open-ended growth of linked data. When an engine like Eyeling solves rule bodies with a Prolog-like prover but still saturates forward consequences, it’s exactly this bridge: Prolog-style execution serving a web-scale, graph-first notion of meaning.
|
|
1970
|
+
|
|
@@ -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
|
+
}.
|
package/examples/fibonacci.n3
CHANGED
|
@@ -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
|
} .
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# N3 graph patch example
|
|
3
|
+
# - Removes every triple from :source that is also present in :delete
|
|
4
|
+
# - Then adds all triples from :insert
|
|
5
|
+
# Uses only N3 builtins from the W3C spec:
|
|
6
|
+
# log:collectAllIn, log:conjunction, log:notIncludes
|
|
7
|
+
# ===================================================================
|
|
8
|
+
|
|
9
|
+
@prefix : <http://example.org/> .
|
|
10
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
11
|
+
|
|
12
|
+
:patch1
|
|
13
|
+
:source {
|
|
14
|
+
:alice a :Person ;
|
|
15
|
+
:age 30 ;
|
|
16
|
+
:status :OldStatus ;
|
|
17
|
+
:email "alice@example.org" .
|
|
18
|
+
:bob a :Person .
|
|
19
|
+
} ;
|
|
20
|
+
:delete {
|
|
21
|
+
:alice :age 30 ;
|
|
22
|
+
:status :OldStatus ;
|
|
23
|
+
:email "alice@example.org" .
|
|
24
|
+
} ;
|
|
25
|
+
:insert {
|
|
26
|
+
:alice :age 31 ;
|
|
27
|
+
:status :ActiveStatus ;
|
|
28
|
+
:verified true .
|
|
29
|
+
} .
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
:patch1 :source ?G ;
|
|
33
|
+
:delete ?Del ;
|
|
34
|
+
:insert ?Ins .
|
|
35
|
+
|
|
36
|
+
# Collect exactly the triples from ?G that are NOT included in the delete graph term ?Del
|
|
37
|
+
( { ?S ?P ?O . }
|
|
38
|
+
{
|
|
39
|
+
?S ?P ?O .
|
|
40
|
+
?Del log:notIncludes { ?S ?P ?O . } .
|
|
41
|
+
}
|
|
42
|
+
?keptTriples
|
|
43
|
+
) log:collectAllIn ?G .
|
|
44
|
+
|
|
45
|
+
# Merge kept graph terms back into a single graph term
|
|
46
|
+
?keptTriples log:conjunction ?keptGraph .
|
|
47
|
+
|
|
48
|
+
# Add the insert graph term
|
|
49
|
+
( ?keptGraph ?Ins ) log:conjunction ?patched .
|
|
50
|
+
}
|
|
51
|
+
=>
|
|
52
|
+
{
|
|
53
|
+
:patch1 :result ?patched .
|
|
54
|
+
} .
|
|
55
|
+
|