deriva 0.0.11 → 0.0.13
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/README.md +2 -1
- package/conformance-report.md +1 -1
- package/docs/assets/deriva-logo.png +0 -0
- package/docs/guide.md +22 -19
- package/docs/language-reference.md +31 -31
- package/package.json +1 -1
- package/src/builtins/arithmetic.js +2 -2
- package/src/builtins/terms.js +1 -1
- package/src/cli.js +1 -1
- package/src/explain.js +2 -2
- package/src/parser.js +1 -1
- package/src/solver.js +2 -2
- package/test/conformance/README.md +3 -3
- package/test/run-conformance-report.mjs +1 -1
- package/test/run-regression.mjs +1 -1
- package/docs/assets/deriva-logo-v1.png +0 -0
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# Deriva
|
|
2
2
|
|
|
3
3
|
<p align="left">
|
|
4
|
-
<img src="docs/assets/deriva-logo.png" alt="Deriva logo" width="
|
|
4
|
+
<img src="docs/assets/deriva-logo.png" alt="Deriva logo" width="70">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/deriva)
|
|
8
|
+
[](https://zenodo.org/badge/latestdoi/1305066220)
|
|
8
9
|
|
|
9
10
|
Deriva is a small reasoning language for turning facts and rules into answers and proofs.
|
|
10
11
|
|
package/conformance-report.md
CHANGED
|
Binary file
|
package/docs/guide.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
This guide introduces Deriva, a small Horn-clause language and engine whose source syntax is Prolog-like but deliberately its own compact language for facts, rules, goals, answers, and proofs. Deriva works over ordinary terms, lists, arithmetic, strings, and finite search. Run it with the `deriva` CLI, or use `node bin/deriva.js` when working directly from a source checkout.
|
|
4
4
|
|
|
5
|
+
This documentation uses **Deriva** for the project, language, and engine, and
|
|
6
|
+
`deriva` for the npm package and CLI command.
|
|
7
|
+
|
|
5
8
|
Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. Web identifiers can be written as ordinary quoted atoms that include angle brackets, for example `'<https://schema.org/name>'`, when a program needs explicit IRI-shaped names without prefix declarations. Deriva output is ordinary Deriva syntax: by default, the CLI materializes selected answer facts and prints those facts only. Pass `--proof` (or `-p`) when you also want each answer followed by a `why/2` explanation fact that records the proof. Programs may add `materialize/2` declarations such as `materialize(answer, 2).` to focus output on selected predicates.
|
|
6
9
|
|
|
7
10
|
Execution is automatically hybrid. Ordinary predicates use indexed
|
|
@@ -15,7 +18,7 @@ For the normative language definition, including lexical syntax, terms, clauses,
|
|
|
15
18
|
## Contents
|
|
16
19
|
|
|
17
20
|
1. [Quick start](#quick-start)
|
|
18
|
-
2. [Running deriva](#running-deriva)
|
|
21
|
+
2. [Running the `deriva` CLI](#running-the-deriva-cli)
|
|
19
22
|
3. [Default output](#default-output)
|
|
20
23
|
4. [Writing programs](#writing-programs)
|
|
21
24
|
5. [Aggregation helpers](#aggregation-helpers)
|
|
@@ -61,7 +64,7 @@ deriva --version
|
|
|
61
64
|
|
|
62
65
|
`npm install -g .` is another local-checkout option if you want npm to install the package globally instead of linking it. Avoid hand-written `/usr/local/bin` symlinks unless you really need one; npm already reads the `bin` entry in `package.json` and creates the correct executable shim.
|
|
63
66
|
|
|
64
|
-
## Running deriva
|
|
67
|
+
## Running the `deriva` CLI
|
|
65
68
|
|
|
66
69
|
The commands in this section use `deriva` for readability. In a source checkout where you have not run `npm link` or `npm install -g .`, replace `deriva` with `node bin/deriva.js`, or run the command through `npm exec --yes --package=. -- deriva`.
|
|
67
70
|
|
|
@@ -72,7 +75,7 @@ deriva --version
|
|
|
72
75
|
deriva -v
|
|
73
76
|
```
|
|
74
77
|
|
|
75
|
-
Run a program and let
|
|
78
|
+
Run a program and let Deriva print derived binary facts:
|
|
76
79
|
|
|
77
80
|
```sh
|
|
78
81
|
deriva examples/ancestor.pl
|
|
@@ -85,7 +88,7 @@ deriva --proof examples/ancestor.pl
|
|
|
85
88
|
deriva -p examples/ancestor.pl
|
|
86
89
|
```
|
|
87
90
|
|
|
88
|
-
|
|
91
|
+
Deriva-readable explanations are opt-in proof output. Each `why/2` fact contains a nested abstract proof term, and a blank line separates consecutive explanations. Using Deriva syntax for explanations keeps them in the same language as the answers themselves: they are readable by humans, parseable by Deriva, easy to test, and can be transformed or explained further like any other Deriva data. For example:
|
|
89
92
|
|
|
90
93
|
```deriva
|
|
91
94
|
type(socrates, mortal).
|
|
@@ -106,7 +109,7 @@ why(
|
|
|
106
109
|
|
|
107
110
|
```
|
|
108
111
|
|
|
109
|
-
The explanation output can itself be read as
|
|
112
|
+
The explanation output can itself be read as Deriva input; for example, another program can materialize `why/2` facts such as `why(type(socrates, mortal), Proof)`. `--proof` adds only these explanation facts; it does not change the answers found by the solver.
|
|
110
113
|
|
|
111
114
|
### Explanation cookbook
|
|
112
115
|
|
|
@@ -181,7 +184,7 @@ ancestor(X, Y) :- parent(X, Y).
|
|
|
181
184
|
ancestor(X, Z) :- parent(X, Y), ancestor(Y, Z).
|
|
182
185
|
```
|
|
183
186
|
|
|
184
|
-
By default,
|
|
187
|
+
By default, Deriva asks for new ground consequences of selected output predicates, suppresses duplicates, excludes source facts, sorts the result, and prints Prolog facts:
|
|
185
188
|
|
|
186
189
|
```deriva
|
|
187
190
|
ancestor(jan, emma).
|
|
@@ -213,7 +216,7 @@ answer(case1, accepted).
|
|
|
213
216
|
|
|
214
217
|
## Writing programs
|
|
215
218
|
|
|
216
|
-
A good
|
|
219
|
+
A good Deriva program normally has three layers:
|
|
217
220
|
|
|
218
221
|
1. source facts;
|
|
219
222
|
2. helper predicates for calculation or search;
|
|
@@ -306,7 +309,7 @@ best_cycle(Cycle, Cost) :-
|
|
|
306
309
|
|
|
307
310
|
## Context data
|
|
308
311
|
|
|
309
|
-
Comma terms can be data as well as conjunctions.
|
|
312
|
+
Comma terms can be data as well as conjunctions. Deriva provides two context utilities:
|
|
310
313
|
|
|
311
314
|
```deriva
|
|
312
315
|
holds((name(alice, "Alice"), knows(alice, bob)), name(S, O)).
|
|
@@ -513,13 +516,13 @@ for f in examples/proof/*.pl; do
|
|
|
513
516
|
done
|
|
514
517
|
```
|
|
515
518
|
|
|
516
|
-
Run the full
|
|
519
|
+
Run the full Deriva suite:
|
|
517
520
|
|
|
518
521
|
```sh
|
|
519
522
|
npm run test:deriva
|
|
520
523
|
```
|
|
521
524
|
|
|
522
|
-
The
|
|
525
|
+
The Deriva corpus runner runs in this order: Conformance, Regression/API/White-box, Examples. Each section prints its own subtotal, followed by a suite-specific grand total. The suite checks the conformance cases derived from the language reference, supplemental regression/API/white-box checks, and every runnable example against its golden output.
|
|
523
526
|
|
|
524
527
|
Run only one internal suite when you are iterating:
|
|
525
528
|
|
|
@@ -537,7 +540,7 @@ node test/run-conformance-report.mjs
|
|
|
537
540
|
|
|
538
541
|
Release preparation runs the same report and writes [`conformance-report.md`](../conformance-report.md), so each published package carries a current conformance summary.
|
|
539
542
|
|
|
540
|
-
The conformance suite lives in [`test/conformance/`](../test/conformance/) as a file-based
|
|
543
|
+
The conformance suite lives in [`test/conformance/`](../test/conformance/) as a file-based Deriva corpus. Positive cases pair `cases/<name>.pl` with exact expected stdout under `expected/<name>.pl`; negative cases pair `errors/<name>.pl` with exact expected error text under `expected-errors/<name>.txt`; warning cases pair `warnings/<name>.pl` with exact `--warnings` stdout and stderr files under `expected-warnings/`; proof cases pair `proofs/<name>.pl` with exact `--proof` output under `expected-proofs/`. Cases may be grouped in category directories such as `arithmetic/`, `strings/`, `lists/`, `terms/`, `atoms/`, `variables/`, `negation/`, and `syntax/`, so another implementation can reuse the same corpus as an executable language contract. The suite covers the standard language surface from the language reference, including reusable built-ins, standard errors, standard warnings, and the machine-readable `why/2` proof-output contract. The regression suite lives in [`test/run-regression.mjs`](../test/run-regression.mjs) and covers CLI regressions, the public JavaScript API, and white-box invariants for parser, unification, and indexing behavior.
|
|
541
544
|
|
|
542
545
|
## Development and release
|
|
543
546
|
|
|
@@ -570,7 +573,7 @@ The `preversion` script reruns the full test suite and refreshes [`conformance-r
|
|
|
570
573
|
|
|
571
574
|
## Relationship to Eyeling
|
|
572
575
|
|
|
573
|
-
[Eyeling](https://github.com/eyereasoner/eyeling) and
|
|
576
|
+
[Eyeling](https://github.com/eyereasoner/eyeling) and Deriva share the same goal of small, inspectable rule-based reasoning in JavaScript, but they make different language and implementation trade-offs.
|
|
574
577
|
|
|
575
578
|
Eyeling is the RDF/Notation3 member of the family. It reads N3-style triples, quoted formulas, forward rules written with `=>`, backward rules written with `<=`, RDF terms, RDF-JS data, and RDF-oriented streams. That makes it the better fit when data interchange with RDF/N3 tools is the main requirement.
|
|
576
579
|
|
|
@@ -581,13 +584,13 @@ A useful rule of thumb:
|
|
|
581
584
|
| Use case | Prefer | Why |
|
|
582
585
|
| --- | --- | --- |
|
|
583
586
|
| RDF/N3 data, triples, prefixes, graph terms, RDF-JS, RDF message streams | Eyeling | The surface language and APIs are RDF/Notation3-native. |
|
|
584
|
-
| Compact relational rules over ordinary terms, lists, arithmetic, and finite search |
|
|
587
|
+
| Compact relational rules over ordinary terms, lists, arithmetic, and finite search | Deriva | The syntax is shorter for non-RDF relation programs and output is ordinary facts. |
|
|
585
588
|
| Human-auditable derivations | Either | Both can emit proof explanations when requested. |
|
|
586
|
-
| Large generated Horn-clause workloads |
|
|
589
|
+
| Large generated Horn-clause workloads | Deriva | The engine specializes in predicate/arity indexing, scalar argument indexes, fast fact paths, and materialized output goals. |
|
|
587
590
|
|
|
588
|
-
On local smoke benchmarks,
|
|
591
|
+
On local smoke benchmarks, Deriva is substantially faster on large generated Horn-clause and recursion-heavy workloads. These numbers are 5-run medians with stdout redirected to `/dev/null`, using Node.js `v22.16.0`, Deriva from this checkout, and Eyeling package version `1.34.6` with its default output mode. The ratio is `Eyeling median / Deriva median`, so larger numbers mean Deriva was faster.
|
|
589
592
|
|
|
590
|
-
| Example |
|
|
593
|
+
| Example | Deriva median | Eyeling median | Ratio |
|
|
591
594
|
| --- | ---: | ---: | ---: |
|
|
592
595
|
| `fundamental-theorem-arithmetic` | `0.16 sec` | `2.00 sec` | `12.66x` |
|
|
593
596
|
| `deep-taxonomy-100000` | `1.69 sec` | `4.72 sec` | `2.79x` |
|
|
@@ -597,13 +600,13 @@ On local smoke benchmarks, deriva is substantially faster on large generated Hor
|
|
|
597
600
|
|
|
598
601
|
Treat these as smoke comparisons rather than a formal benchmark: hardware, Node.js version, package version, CLI startup, and output mode all matter.
|
|
599
602
|
|
|
600
|
-
The projects are therefore complementary rather than replacements for each other: Eyeling optimizes for Semantic Web interoperability and N3 expressiveness;
|
|
603
|
+
The projects are therefore complementary rather than replacements for each other: Eyeling optimizes for Semantic Web interoperability and N3 expressiveness; Deriva optimizes for a small standard-looking relational rule language and fast finite goal-directed execution.
|
|
601
604
|
|
|
602
605
|
## Performance notes
|
|
603
606
|
|
|
604
607
|
Use `-s` or `--stats` for a quick sanity check while optimizing solver changes. It prints counters such as `solve_goals_calls`, `unify_calls`, `deterministic_rule_expansions`, `candidate_lists_selected`, `clause_candidates_considered`, `clauses_tried`, `max_depth`, and `max_solver_call_depth` to stderr, leaving normal output stable for golden-file tests. The `max_solver_call_depth` counter is especially useful for browser regressions, where the VM call stack can be tighter than a command-line run. Use `-w` or `--warnings` separately when you want portability diagnostics without enabling stricter parsing.
|
|
605
608
|
|
|
606
|
-
|
|
609
|
+
Deriva hashes predicate groups by name and arity, then indexes clauses by scalar argument values. It also builds two-argument composite indexes for scalar pairs and probes those composite indexes without per-lookup heap allocation. This helps both large generated programs with many predicates and selective queries such as:
|
|
607
610
|
|
|
608
611
|
```deriva
|
|
609
612
|
edge(g1, a, X).
|
|
@@ -611,7 +614,7 @@ path(a, Y).
|
|
|
611
614
|
status(Case, accepted).
|
|
612
615
|
```
|
|
613
616
|
|
|
614
|
-
Ground facts use a fast path that avoids freshening and copying a rule body. Recursive-predicate detection uses an explicit work stack, which keeps large predicate chains safer in the browser.
|
|
617
|
+
Ground facts use a fast path that avoids freshening and copying a rule body. Recursive-predicate detection uses an explicit work stack, which keeps large predicate chains safer in the browser. Deriva automatically tables positive recursive groups, including dependencies reached through supported meta-goals and directly materialized relations. Cyclic calls iterate to an answer fixed point before replay. Recursive components containing a negative dependency retain guarded ordinary resolution because positive fixed-point tabling is not a semantics for unstratified negation. The engine also infers common structurally decreasing input positions; calls with an unbound structural input and fully open calls retain ordinary resolution rather than trying to materialize a potentially infinite relation. Authors do not need a search-control declaration.
|
|
615
618
|
|
|
616
619
|
Predicates can also carry advisory mode and determinism declarations for documentation and host tooling:
|
|
617
620
|
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
- [11.1 Automatic hybrid reasoning](#111-automatic-hybrid-reasoning)
|
|
51
51
|
- [11.2 Default-output materialization](#112-default-output-materialization)
|
|
52
52
|
- [11.3 Advisory modes and determinism](#113-advisory-modes-and-determinism)
|
|
53
|
-
- [12.
|
|
53
|
+
- [12. Deriva Sockets](#12-deriva-sockets)
|
|
54
54
|
- [12.1 Socket vocabulary](#121-socket-vocabulary)
|
|
55
55
|
- [12.2 Socket example](#122-socket-example)
|
|
56
56
|
- [12.3 Sockets and AI agents](#123-sockets-and-ai-agents)
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
|
|
68
68
|
## Abstract
|
|
69
69
|
|
|
70
|
-
Deriva is a compact definite-clause language whose surface syntax is Prolog-like term and clause syntax with deliberate
|
|
70
|
+
Deriva is a compact definite-clause language whose surface syntax is Prolog-like term and clause syntax with deliberate Deriva choices for rule-based programs over ordinary terms, lists, arithmetic, strings, and finite search. A Deriva program is a finite sequence of facts and Horn clauses. The underlying declarative semantics of the pure language is **Herbrand semantics**: constants, compound terms, and lists denote themselves, and predicates denote sets of ground atomic formulas over those terms. Evaluation is goal-directed: goals are solved by unification against facts, rules, and a fixed set of built-in predicates.
|
|
71
71
|
|
|
72
72
|
Deriva is intentionally smaller than ISO Prolog. It supports compact Horn-clause reasoning, list processing, arithmetic examples, finite search, and context data, without operators, cut, modules, dynamic predicates, DCGs, zero-arity compound syntax, or a complete ISO standard library.
|
|
73
73
|
|
|
@@ -89,7 +89,7 @@ A **goal** is an atomic formula, a built-in call, or a comma conjunction.
|
|
|
89
89
|
|
|
90
90
|
A **source fact** is a fact written directly in the input program. A **new derivation** is a ground consequence found through at least one rule and not merely repeated from the source facts.
|
|
91
91
|
|
|
92
|
-
The **Herbrand universe** of a program is the set of all ground
|
|
92
|
+
The **Herbrand universe** of a program is the set of all ground Deriva terms constructible from the constants and functors in the program, together with the built-in list constructors `[]` and `./2` where lists are used. The **Herbrand base** is the set of all ground atomic formulas whose predicate symbols occur in the program and whose arguments are terms from the Herbrand universe.
|
|
93
93
|
|
|
94
94
|
## 2. Design goals
|
|
95
95
|
|
|
@@ -329,7 +329,7 @@ pair(3, nested(atom, [x, y]))
|
|
|
329
329
|
|
|
330
330
|
The same concrete syntax is used for atomic formulas when the compound appears as a fact, rule head, or goal. In `parent(pat, jan).`, `parent/2` is a predicate symbol and the whole expression is an atomic formula. In `value(x, parent(pat, jan)).`, the inner `parent(pat, jan)` is ordinary compound data.
|
|
331
331
|
|
|
332
|
-
The functor or predicate name is fixed syntactically and is written as an atom constant.
|
|
332
|
+
The functor or predicate name is fixed syntactically and is written as an atom constant. Deriva does not support variables in predicate or functor position.
|
|
333
333
|
|
|
334
334
|
### 5.4 Lists
|
|
335
335
|
|
|
@@ -375,7 +375,7 @@ Clauses with the same predicate name and arity define one predicate group. Predi
|
|
|
375
375
|
|
|
376
376
|
## 7. Goals and proof search
|
|
377
377
|
|
|
378
|
-
Goals are solved left-to-right. For a user-defined atomic-formula goal,
|
|
378
|
+
Goals are solved left-to-right. For a user-defined atomic-formula goal, Deriva selects candidate clauses by predicate name, arity, and available indexes. A candidate clause is freshened, its head is unified with the goal, and then its body is solved.
|
|
379
379
|
|
|
380
380
|
A conjunction goal succeeds when all conjunct goals succeed in order. An answer is printed as the resolved answer term followed by a period.
|
|
381
381
|
|
|
@@ -385,11 +385,11 @@ Unification follows the ordinary first-order term structure used by the language
|
|
|
385
385
|
|
|
386
386
|
### 7.2 Failure
|
|
387
387
|
|
|
388
|
-
A goal fails when no built-in case or user clause can prove it.
|
|
388
|
+
A goal fails when no built-in case or user clause can prove it. Deriva has no exception term language; parse errors and resource failures are implementation errors reported to the host.
|
|
389
389
|
|
|
390
390
|
### 7.3 Finite search expectation
|
|
391
391
|
|
|
392
|
-
Programs and selected output goals SHOULD be written so the relevant search space is finite.
|
|
392
|
+
Programs and selected output goals SHOULD be written so the relevant search space is finite. Deriva includes recursion guards and tabling support, but it is not required to terminate for arbitrary recursive logic programs.
|
|
393
393
|
|
|
394
394
|
## 8. Logical reading: Herbrand semantics
|
|
395
395
|
|
|
@@ -415,9 +415,9 @@ Equivalently, the least Herbrand model is obtained by repeatedly applying the im
|
|
|
415
415
|
|
|
416
416
|
### 8.1 Variables and quantification
|
|
417
417
|
|
|
418
|
-
Variables do not range over external objects, records, pointers, or host-language values. In the logical reading, variables range over Herbrand terms. A rule is implicitly universally quantified over its variables. A selected goal is existential in the usual logic-programming sense:
|
|
418
|
+
Variables do not range over external objects, records, pointers, or host-language values. In the logical reading, variables range over Herbrand terms. A rule is implicitly universally quantified over its variables. A selected goal is existential in the usual logic-programming sense: Deriva searches for substitutions of its variables by Herbrand terms that make the goal true with respect to the program.
|
|
419
419
|
|
|
420
|
-
|
|
420
|
+
Deriva has no blank nodes and no existential variables in rule heads. Existential-style consequences SHOULD be represented by explicit Herbrand witness terms written directly in rule heads:
|
|
421
421
|
|
|
422
422
|
```deriva
|
|
423
423
|
has_parent(Child, parent_of(Child)) :-
|
|
@@ -433,11 +433,11 @@ These rules may derive `parent_of(alice)` or `registration_of(alice, logic)` as
|
|
|
433
433
|
|
|
434
434
|
Because the domain is Herbrand, equality in the pure language is syntactic identity of terms after substitution. Two distinct atom constants are distinct. Two compound terms are equal only when they have the same functor, the same arity, and pairwise equal arguments. Lists follow the same rule through their `[]` and `./2` representation.
|
|
435
435
|
|
|
436
|
-
Operationally,
|
|
436
|
+
Operationally, Deriva uses first-order unification to find substitutions. The implementation does not perform an occurs check, so cyclic terms are not part of the portable Herbrand reading even if a particular implementation can temporarily construct recursive bindings internally. Portable programs SHOULD avoid relying on occurs-check-sensitive cases such as `eq(X, f(X))`.
|
|
437
437
|
|
|
438
438
|
### 8.3 Goal-directed execution versus model-theoretic meaning
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
Deriva's CLI and library evaluator are goal-directed. They try to prove requested goals by resolving them against facts, rules, and built-ins, using clause order, goal order, indexing, tabling, and deterministic built-in execution. This operational strategy is intended to enumerate answers that are true in the least Herbrand model for the pure Horn-clause fragment, but it is not a complete bottom-up model enumerator. Non-terminating recursion or infinite generators can prevent an answer from being found even when the answer belongs to the least Herbrand model.
|
|
441
441
|
|
|
442
442
|
Default CLI output is also a host behavior, not a separate semantics. It asks broad materialization goals, suppresses duplicates, excludes source facts, keeps ground answers, and prints selected consequences. Embedders can still access the goal-directed solver directly through the implementation API.
|
|
443
443
|
|
|
@@ -445,7 +445,7 @@ Default CLI output is also a host behavior, not a separate semantics. It asks br
|
|
|
445
445
|
|
|
446
446
|
Built-ins are specified relations or operations added to the Herbrand core. A built-in call in a goal has the syntax of an atomic formula, but its success relation is specified procedurally here rather than by source clauses. Some built-ins, such as `eq/2`, `append/3`, `member/2`, and `length/2`, can be understood as relations over Herbrand terms. Others, such as arithmetic, string matching, date/time predicates, aggregation, `once/1`, and negation-as-failure, are operational extensions whose behavior is defined by this specification rather than by pure least-Herbrand-model semantics alone.
|
|
447
447
|
|
|
448
|
-
Arithmetic and string built-ins do not introduce a separate semantic universe. They inspect the lexical values of already represented Herbrand constants and, when they succeed, bind output arguments to
|
|
448
|
+
Arithmetic and string built-ins do not introduce a separate semantic universe. They inspect the lexical values of already represented Herbrand constants and, when they succeed, bind output arguments to Deriva terms such as numbers, strings, or atom constants. For example, `add(2, 3, X)` may bind `X` to the number term `5`; it does not mean that variables range over host-language numbers outside the Herbrand universe.
|
|
449
449
|
|
|
450
450
|
Negation-as-failure `not(Goal)` is especially operational: it succeeds when the current goal-directed search finds no solution for `Goal`. It is not classical negation and should not be read as adding negative facts to the Herbrand model. Programs using negation SHOULD keep the negated goal sufficiently ground and finite.
|
|
451
451
|
|
|
@@ -544,7 +544,7 @@ Comparisons interpret numeric-looking terms numerically. Other scalar terms are
|
|
|
544
544
|
| `lowercase(Text, Out)`, `uppercase(Text, Out)`, `trim(Text, Out)` | Text normalization helpers. |
|
|
545
545
|
| `number_string(Number, String)` | Converts a number to a string or parses a numeric string into a number. |
|
|
546
546
|
| `atom_string(Atom, String)` | Converts between atom constants and strings. |
|
|
547
|
-
| `term_string(Term, String)` | Renders a ground term as its
|
|
547
|
+
| `term_string(Term, String)` | Renders a ground term as its Deriva source string. |
|
|
548
548
|
|
|
549
549
|
### 9.7 Lists
|
|
550
550
|
|
|
@@ -616,15 +616,15 @@ The first goal can yield `holds((name(alice, "Alice"), knows(alice, bob)), name(
|
|
|
616
616
|
|
|
617
617
|
## 10. Implementation-specific built-ins
|
|
618
618
|
|
|
619
|
-
Implementations MAY provide additional built-ins beyond the standard predicates listed above. Such built-ins are **implementation-specific built-ins**. They are useful for embedding
|
|
619
|
+
Implementations MAY provide additional built-ins beyond the standard predicates listed above. Such built-ins are **implementation-specific built-ins**. They are useful for embedding Deriva in particular host environments, exposing efficient finite-domain solvers, or providing domain-specific relations for applications.
|
|
620
620
|
|
|
621
|
-
Implementation-specific built-ins are not required for conformance to this specification. A portable
|
|
621
|
+
Implementation-specific built-ins are not required for conformance to this specification. A portable Deriva program SHOULD NOT depend on one unless the target implementation explicitly documents it.
|
|
622
622
|
|
|
623
623
|
An implementation-specific built-in SHOULD obey the same surface-language discipline as standard built-ins:
|
|
624
624
|
|
|
625
625
|
- it is called using ordinary atomic-formula syntax, for example `some_extension(A, B)`;
|
|
626
|
-
- its arguments and results are
|
|
627
|
-
- it succeeds, fails, and binds variables as a relation over
|
|
626
|
+
- its arguments and results are Deriva terms from the Herbrand universe;
|
|
627
|
+
- it succeeds, fails, and binds variables as a relation over Deriva terms;
|
|
628
628
|
- it SHOULD document its intended modes, especially which arguments must be ground before it runs deterministically;
|
|
629
629
|
- it MUST NOT change the meaning of ordinary facts, rules, unification, or standard built-ins.
|
|
630
630
|
|
|
@@ -638,7 +638,7 @@ Declarations are written as ordinary facts, but the host treats them specially.
|
|
|
638
638
|
|
|
639
639
|
### 11.1 Automatic hybrid reasoning
|
|
640
640
|
|
|
641
|
-
|
|
641
|
+
Deriva automatically combines ordinary goal-directed resolution with tabled
|
|
642
642
|
resolution. Predicate dependency cycles are detected when a program is loaded,
|
|
643
643
|
including dependencies inside conjunctions, negation, `once/1`, `forall/2`, and
|
|
644
644
|
aggregation goals. Positive recursive predicate groups, including directly
|
|
@@ -702,9 +702,9 @@ The example documents the common checking/generation mode where the list is supp
|
|
|
702
702
|
|
|
703
703
|
## 12. Deriva Sockets
|
|
704
704
|
|
|
705
|
-
A **
|
|
705
|
+
A **Deriva Socket** is a declared semantic opening in a Deriva program where facts, rules, tools, datasets, or agents can plug in knowledge through an explicit contract while preserving Deriva-readable reasoning and explanations.
|
|
706
706
|
|
|
707
|
-
The term follows the ordinary socket pattern: a socket defines a place where a matching provider can connect. In
|
|
707
|
+
The term follows the ordinary socket pattern: a socket defines a place where a matching provider can connect. In Deriva, the matching part is knowledge. A socket identifies what shape of knowledge a program expects; a plug identifies which provider supplies it. This separates reasoning logic from knowledge providers and makes composition boundaries visible as Deriva data.
|
|
708
708
|
|
|
709
709
|
In this specification, sockets are a portable **programming pattern** expressed with ordinary facts. The core solver does not give `socket/2`, `plug/2`, `provides/1`, or `requires/1` special proof-search behavior unless a host explicitly documents such an extension. Because they are ordinary facts, socket declarations remain readable, inspectable, explainable, and safe to ignore by hosts that do not validate them.
|
|
710
710
|
|
|
@@ -719,7 +719,7 @@ provides(Signature).
|
|
|
719
719
|
requires(Signature).
|
|
720
720
|
```
|
|
721
721
|
|
|
722
|
-
`Name` and `Provider` are ordinary
|
|
722
|
+
`Name` and `Provider` are ordinary Deriva terms, usually atom constants. `Contract` is an ordinary Deriva term that describes the expected or offered knowledge. A portable signature form is:
|
|
723
723
|
|
|
724
724
|
```deriva
|
|
725
725
|
predicate(Predicatename, Arity)
|
|
@@ -755,21 +755,21 @@ ancestor(X, Z) :-
|
|
|
755
755
|
ancestor(Y, Z).
|
|
756
756
|
```
|
|
757
757
|
|
|
758
|
-
The `ancestor/2` rules do not depend on a particular storage mechanism for `parent/2`. In a small test, the provider may be the same file. In an embedded host, it may be a database adapter, a document extractor, a remote service, or another
|
|
758
|
+
The `ancestor/2` rules do not depend on a particular storage mechanism for `parent/2`. In a small test, the provider may be the same file. In an embedded host, it may be a database adapter, a document extractor, a remote service, or another Deriva module. The socket facts make that boundary explicit without changing the logical meaning of the rules.
|
|
759
759
|
|
|
760
|
-
When
|
|
760
|
+
When Deriva derives `ancestor(pat, emma)`, the answer explanation can still refer to the source clauses that were actually used, for example facts for `parent/2` and rules for `ancestor/2`. The socket facts add an inspectable description of where such knowledge is intended to enter.
|
|
761
761
|
|
|
762
762
|
### 12.3 Sockets and AI agents
|
|
763
763
|
|
|
764
|
-
|
|
764
|
+
Deriva Sockets are especially useful for AI-facing systems. An AI agent can extract or propose candidate claims, but those claims should enter a reasoning program as explicit Deriva facts or rules through a declared socket rather than as opaque text. Deriva can then check the claims against other facts and rules, derive consequences, and optionally return ordinary `why/2` explanations.
|
|
765
765
|
|
|
766
|
-
This gives a clear division of labor: AI can help generate, translate, and connect knowledge;
|
|
766
|
+
This gives a clear division of labor: AI can help generate, translate, and connect knowledge; Deriva can represent, check, and explain the reasoning; sockets define the boundary between them.
|
|
767
767
|
|
|
768
768
|
## 13. Output and read-back profile
|
|
769
769
|
|
|
770
770
|
Normal answer output prints one resolved answer term followed by a period. Strings are double-quoted; atom constants are quoted when needed; lists use list syntax; compound terms use functor notation. Host interfaces MAY provide an option such as `--proof` to add `why/2` explanation facts; this option MUST NOT change the answers found. Host interfaces MAY also provide a non-fatal warning option such as `--warnings` for portability diagnostics such as unstratified negation; this option MUST NOT change the answers found.
|
|
771
771
|
|
|
772
|
-
Output SHOULD be accepted as
|
|
772
|
+
Output SHOULD be accepted as Deriva input when it contains only supported term syntax. Explanations are ordinary Deriva facts, so answer output can be read back and processed by Deriva.
|
|
773
773
|
|
|
774
774
|
Default host output behavior is:
|
|
775
775
|
|
|
@@ -783,11 +783,11 @@ Default host output behavior is:
|
|
|
783
783
|
|
|
784
784
|
### 13.1 Explanation output
|
|
785
785
|
|
|
786
|
-
When proof output is enabled, each answer SHOULD be followed by a machine-readable `why/2` fact. Explanation output is ordinary
|
|
786
|
+
When proof output is enabled, each answer SHOULD be followed by a machine-readable `why/2` fact. Explanation output is ordinary Deriva syntax whose second argument is a nested abstract proof term such as `proof(goal(G), by(Method), bindings(Bindings), uses(Proofs))`; implementations SHOULD print `goal(...)` and `by(...)` on separate lines for readability. A proof term preserves the answer goal, derivation method, relevant bindings, and nested uses while omitting proof IDs. User clauses SHOULD be referenced explicitly as `fact(Filename, clause(N))` or `rule(Filename, clause(N))`, where `N` is the 1-based clause number within that source. Built-ins SHOULD be referenced as `builtin(Name, Arity)` because they do not come from source clauses. Explanation output is outside the logical semantics of the input program and MUST NOT change the set of answers.
|
|
787
787
|
|
|
788
788
|
## 14. Conformance
|
|
789
789
|
|
|
790
|
-
A conforming
|
|
790
|
+
A conforming Deriva implementation supports the standard language described above as one conformance surface rather than as separate core and extension profiles. This includes:
|
|
791
791
|
|
|
792
792
|
- lexical syntax described above;
|
|
793
793
|
- facts and definite clauses;
|
|
@@ -804,11 +804,11 @@ A conforming deriva implementation supports the standard language described abov
|
|
|
804
804
|
|
|
805
805
|
Browser execution, package layout, CLI URL loading, and any implementation-specific built-ins described in host documentation are outside this conformance surface unless separately standardized.
|
|
806
806
|
|
|
807
|
-
Conformance cases live in the repository under `test/conformance/`. They are run by `npm test` before the example suite, and can be run alone with `node test/run-conformance.mjs`. Positive cases have input programs under `test/conformance/cases/` and exact expected standard-output files under `test/conformance/expected/`; both use `.pl` so expected output remains
|
|
807
|
+
Conformance cases live in the repository under `test/conformance/`. They are run by `npm test` before the example suite, and can be run alone with `node test/run-conformance.mjs`. Positive cases have input programs under `test/conformance/cases/` and exact expected standard-output files under `test/conformance/expected/`; both use `.pl` so expected output remains Deriva-readable. Expected-error cases live under `test/conformance/errors/` with exact messages under `test/conformance/expected-errors/`. Expected-warning cases live under `test/conformance/warnings/` with exact `--warnings` stdout and stderr files under `test/conformance/expected-warnings/`. Proof cases live under `test/conformance/proofs/` with exact explanation output under `test/conformance/expected-proofs/`. The corpus is grouped by language area, including arithmetic, strings, lists, terms, atoms, variables, negation, declarations, materialization, rules, syntax, and errors.
|
|
808
808
|
|
|
809
809
|
## 15. Relationship to ISO Prolog
|
|
810
810
|
|
|
811
|
-
|
|
811
|
+
Deriva source is intended to be familiar to Prolog readers and uses ISO Prolog-compatible variable and quoted-atom spelling, but Deriva is not ISO Prolog. Notable differences include:
|
|
812
812
|
- no operators or operator declarations;
|
|
813
813
|
- no zero-arity compound syntax such as `nil()`;
|
|
814
814
|
- no cut;
|
|
@@ -819,7 +819,7 @@ deriva source is intended to be familiar to Prolog readers and uses ISO Prolog-c
|
|
|
819
819
|
- no variables in functor or predicate position;
|
|
820
820
|
- no occurs check in unification.
|
|
821
821
|
|
|
822
|
-
Programs intended to be portable to
|
|
822
|
+
Programs intended to be portable to Deriva SHOULD use uppercase or underscore variables, avoid ISO-specific features that Deriva does not implement, and keep terms explicit. Atom names that are not plain lowercase-starting names or graphic atom tokens SHOULD be written as quoted atoms, for example `'a-b'` or `'<abc>'`.
|
|
823
823
|
|
|
824
824
|
## 16. Examples
|
|
825
825
|
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Numeric builtins for integer-preserving arithmetic, floating point functions, comparisons, and ranges.
|
|
2
|
-
// The code keeps BigInt paths where possible so large
|
|
2
|
+
// The code keeps BigInt paths where possible so large Deriva integers remain exact.
|
|
3
3
|
import { compareIntegerText, deref, isDecimalInteger, lexicalValue, numberTerm, numberTextFromDouble, parseFiniteNumber, unify } from '../term.js';
|
|
4
4
|
|
|
5
5
|
const unaryNames = ['neg', 'abs', 'sin', 'cos', 'tan', 'asin', 'acos', 'sqrt', 'floor', 'ceiling', 'trunc', 'rounded', 'exp', 'log'];
|
|
@@ -200,7 +200,7 @@ function nextDown(value) {
|
|
|
200
200
|
function logCompat(input) {
|
|
201
201
|
const value = Math.log(input);
|
|
202
202
|
// V8 and glibc libm differ by one ulp for a few values reached by the
|
|
203
|
-
// Newton-Raphson example. Align this pure-JS port with the native
|
|
203
|
+
// Newton-Raphson example. Align this pure-JS port with the native Deriva
|
|
204
204
|
// reference while leaving ordinary log(1), log(2), log(10), and log(e) alone.
|
|
205
205
|
if (input > 2.5 && input < 2.7182818 && value > 0.95 && value < 1.0) return nextUp(value);
|
|
206
206
|
if (input > 2.7182818 && input < 2.718281828459 && value > 0.999999999999 && value < 1.0) return nextDown(value);
|
package/src/builtins/terms.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Term-inspection builtins for reusable meta-programming over
|
|
1
|
+
// Term-inspection builtins for reusable meta-programming over Deriva terms.
|
|
2
2
|
import { atom, compound, deref, listFromItems, lexicalValue, numberTerm, properListItems, stringTerm, unify } from '../term.js';
|
|
3
3
|
|
|
4
4
|
export const termBuiltins = {
|
package/src/cli.js
CHANGED
package/src/explain.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Deriva proof output helpers.
|
|
2
2
|
// The explanation printer replays a successful goal against the program and emits
|
|
3
|
-
// ordinary
|
|
3
|
+
// ordinary Deriva facts with nested proof terms. Explanations are therefore both
|
|
4
4
|
// human-readable and machine-readable.
|
|
5
5
|
import { COMPOUND, Env, Term, VAR, deref, flattenConjunction, freshTerm, termToString, unify, variantTerms } from './term.js';
|
|
6
6
|
import { selectClauseCandidates } from './program.js';
|
package/src/parser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Tokenizer and recursive-descent parser for the
|
|
1
|
+
// Tokenizer and recursive-descent parser for the Deriva source language.
|
|
2
2
|
// It preserves the compact Prolog-like syntax while producing Term objects for the solver.
|
|
3
3
|
import { atom, compound, cons, emptyList, numberTerm, stringTerm, variable } from './term.js';
|
|
4
4
|
|
package/src/solver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Depth-first
|
|
1
|
+
// Depth-first Deriva solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
2
|
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
3
|
import { COMPOUND, Env, compound, copyResolved, flattenConjunction, freshTerm, termIsGround, termToString, unify, variantTerms } from './term.js';
|
|
4
4
|
import { createDefaultRegistry } from './builtins/registry.js';
|
|
@@ -113,7 +113,7 @@ export class Solver {
|
|
|
113
113
|
continue;
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
-
//
|
|
116
|
+
// Deriva normally solves left-to-right, but ready deterministic builtins can
|
|
117
117
|
// be run early as pure filters. Stop at internal sentinels so rule-body
|
|
118
118
|
// active guards are released before the caller's remaining goals are seen.
|
|
119
119
|
const selectedIndex = selectReadyDeterministicBuiltin(goals, env, this.registry);
|
|
@@ -9,7 +9,7 @@ All conformance files live under topic directories such as `arithmetic/`, `lists
|
|
|
9
9
|
A normal positive case consists of:
|
|
10
10
|
|
|
11
11
|
- `conformance/cases/<name>.pl` — input program;
|
|
12
|
-
- `conformance/expected/<name>.pl` — exact expected standard output, stored as
|
|
12
|
+
- `conformance/expected/<name>.pl` — exact expected standard output, stored as Deriva-readable facts.
|
|
13
13
|
|
|
14
14
|
Expected-error cases consist of:
|
|
15
15
|
|
|
@@ -63,9 +63,9 @@ The runner executes normal materialized programs in-process through the public J
|
|
|
63
63
|
|
|
64
64
|
## Scope
|
|
65
65
|
|
|
66
|
-
The conformance corpus is a single
|
|
66
|
+
The conformance corpus is a single Deriva suite. It covers the standard language described by the language reference: lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification, left-to-right goal-directed proof search, materialized output, read-back printing, standard built-ins, declarations, warnings, errors, proof output, and standard host behavior.
|
|
67
67
|
|
|
68
|
-
The suite deliberately does not separate `core` and `extension` profiles. Reusable built-ins such as arithmetic, strings, lists, aggregation, context terms, term inspection, and search control are part of the standard
|
|
68
|
+
The suite deliberately does not separate `core` and `extension` profiles. Reusable built-ins such as arithmetic, strings, lists, aggregation, context terms, term inspection, and search control are part of the standard Deriva conformance surface. Implementation-specific built-ins may still exist in downstream hosts, but they should have their own tests outside this corpus unless they are standardized.
|
|
69
69
|
|
|
70
70
|
## Updating expected output
|
|
71
71
|
|
|
@@ -56,7 +56,7 @@ export function buildConformanceReport() {
|
|
|
56
56
|
|
|
57
57
|
export function formatConformanceReport(report = buildConformanceReport()) {
|
|
58
58
|
const lines = [
|
|
59
|
-
'#
|
|
59
|
+
'# Deriva conformance report',
|
|
60
60
|
'',
|
|
61
61
|
'This report summarizes the file-based conformance corpus under `test/conformance/`.',
|
|
62
62
|
'',
|
package/test/run-regression.mjs
CHANGED
|
@@ -358,7 +358,7 @@ function documentationSyncCases() {
|
|
|
358
358
|
run: () => assertArrayEqual(findBrokenDocLinks(), [], 'broken documentation links'),
|
|
359
359
|
},
|
|
360
360
|
{
|
|
361
|
-
name: 'documentation uses
|
|
361
|
+
name: 'documentation uses Deriva source style',
|
|
362
362
|
run: () => assertArrayEqual(documentationSourceStyleIssues(), [], 'documentation source style'),
|
|
363
363
|
},
|
|
364
364
|
{
|
|
Binary file
|