eyeprolog 1.5.3 → 1.5.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/README.md CHANGED
@@ -57,10 +57,10 @@ printf 'human(socrates).\nmortal(X) :- human(X).\n' |
57
57
  ```
58
58
 
59
59
  ## Native forward rules (`:+`)
60
-
61
60
  Normal mode executes top-level `Conclusion :+ Premise` rules natively when no explicit
62
61
  `-g/--goal` is supplied. `true :+ Goal` prints answers and `false :+ Goal` emits a
63
- `fuse/1`; JavaScript `run()` follows the same rule. Strict ISO mode disables this
62
+ `fuse/1`; JavaScript `run()` follows the same rule. The bundled `library(eyelet)`
63
+ exports the `:+` operator, `stable/1`, and `becomes/2`. Strict ISO mode disables this
64
64
  extension. Bundled `src/lib/` predicates autoload in files, CLI/API goals, and the REPL;
65
65
  use `--no-autoload` to require explicit imports, and explicitly import libraries that introduce
66
66
  operators before using their syntax. See [*The Art of EyeProlog*](the-art-of-eyeprolog.md) for the full semantics.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.3",
6
+ "version": "1.5.4",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/execute.js CHANGED
@@ -82,8 +82,6 @@ export function executeForwardRules(program, solver, {
82
82
  predeclareForwardTerms(program, solver, group);
83
83
  setSingletonFact(program, 'closure', [numberTerm(0)]);
84
84
  setSingletonFact(program, 'limit', [numberTerm(-1)]);
85
- setSingletonFact(program, 'count', [atom('fm'), numberTerm(0)], true);
86
- setSingletonFact(program, 'count', [atom('mf'), numberTerm(0)], true);
87
85
 
88
86
  const seenAnswers = new Set();
89
87
  let rounds = 0;
@@ -188,11 +186,6 @@ export function executeForwardRules(program, solver, {
188
186
  break;
189
187
  }
190
188
 
191
- for (const name of ['fm', 'mf']) {
192
- const count = keyedCount(program, name);
193
- if (count !== 0) onDiagnostic(`*** ${name}=${count}\n`);
194
- }
195
-
196
189
  return { haltCode, rounds, derived };
197
190
  }
198
191
 
@@ -370,12 +363,4 @@ function singletonInteger(program, name, fallback) {
370
363
  return fallback;
371
364
  }
372
365
 
373
- function keyedCount(program, key) {
374
- const group = program.findGroup('count', 2, 'user');
375
- if (group == null) return 0;
376
- for (let i = group.clauses.length - 1; i >= 0; i--) {
377
- const args = group.clauses[i].head?.args;
378
- if (args?.[0]?.type === ATOM && args[0].name === key && args[1]?.type === NUMBER) return Number(args[1].name);
379
- }
380
- return 0;
381
- }
366
+
@@ -0,0 +1,43 @@
1
+ % Eyelet forward-reasoning support for EyeProlog.
2
+ %
3
+ % The :+/2 closure itself is executed natively by EyeProlog. This module owns
4
+ % the two state helpers used by Eyelet programs and exports the :+ operator so
5
+ % source files can opt into the complete surface with one use_module/1.
6
+
7
+ :- module(eyelet, [op(1200, xfx, :+), stable/1, becomes/2]).
8
+
9
+ :- use_module(library(iso_ext), [forall/2]).
10
+ :- use_module(library(lists), [member/2]).
11
+
12
+ :- meta_predicate(becomes(0, 0)).
13
+
14
+ % stable(+Level)
15
+ % Fail until the native forward-rule driver has reached the requested closure
16
+ % level. Asking for a higher level extends the driver's target limit.
17
+ stable(Level) :-
18
+ call(user:limit(Limit)),
19
+ ( Limit < Level
20
+ -> user:retract(limit(Limit)),
21
+ user:assertz(limit(Level))
22
+ ; true
23
+ ),
24
+ call(user:closure(Closure)),
25
+ Level =< Closure.
26
+
27
+ % becomes(:From, :To)
28
+ % Linear implication over dynamic state: prove and retract every conjunct in
29
+ % From, then assert every conjunct in To.
30
+ becomes(From, To) :-
31
+ catch(call(user:From), _, fail),
32
+ conj_list(From, Old),
33
+ forall(member(Clause, Old), user:retract(Clause)),
34
+ conj_list(To, New),
35
+ forall(member(Clause, New), user:assertz(Clause)).
36
+
37
+ conj_list(true, []).
38
+ conj_list(Goal, [Goal]) :-
39
+ Goal \= (_, _),
40
+ Goal \= false,
41
+ !.
42
+ conj_list((A, B), [A|Rest]) :-
43
+ conj_list(B, Rest).
@@ -69,6 +69,7 @@ export const eyePrologLibraryAutoload = Object.freeze({
69
69
  "bb_get/2": "debug",
70
70
  "bb_global_get/2": "debug",
71
71
  "bb_put/2": "debug",
72
+ "becomes/2": "eyelet",
72
73
  "between/3": "between",
73
74
  "call_nth/2": "iso_ext",
74
75
  "call_residue_vars/2": "atts",
@@ -264,6 +265,7 @@ export const eyePrologLibraryAutoload = Object.freeze({
264
265
  "slice/4": "lists",
265
266
  "smallest_divisor_from/3": "primes",
266
267
  "split/3": "strings",
268
+ "stable/1": "eyelet",
267
269
  "start_tabling/2": "tabling",
268
270
  "string_concat/3": "strings",
269
271
  "substring/4": "strings",
@@ -322,6 +324,7 @@ export const eyePrologLibraryAutoloadModules = Object.freeze([
322
324
  "debug",
323
325
  "dif",
324
326
  "error",
327
+ "eyelet",
325
328
  "format",
326
329
  "freeze",
327
330
  "gensym",
package/src/program.js CHANGED
@@ -1131,6 +1131,14 @@ function bundledLibraryModule(program, module) {
1131
1131
  function groupDependencies(group) {
1132
1132
  const dependencies = [];
1133
1133
  for (const clause of group.clauses) {
1134
+ // Native forward rules store their executable premise in the :+/2 head
1135
+ // rather than in the ordinary clause body. Treat that premise as a goal
1136
+ // dependency so bundled predicates such as stable/1 and becomes/2 can be
1137
+ // autoloaded exactly like calls in ordinary rules. Derived :+ rules may
1138
+ // themselves occur in a conclusion, so recurse through that shape too.
1139
+ if (group.name === ':+') {
1140
+ dependencies.push(...forwardRuleDependencies(clause.head));
1141
+ }
1134
1142
  if (isCompactBinaryClause(clause)) {
1135
1143
  if (clause.bodyName != null) {
1136
1144
  dependencies.push({
@@ -1149,6 +1157,103 @@ function groupDependencies(group) {
1149
1157
  return dependencies;
1150
1158
  }
1151
1159
 
1160
+ function forwardRuleDependencies(term, out = []) {
1161
+ if (term?.type !== COMPOUND) return out;
1162
+ if (term.name === ':+' && term.arity === 2) {
1163
+ out.push(...collectGoalDependencies(term.args[1], false, true));
1164
+ forwardRuleDependencies(term.args[0], out);
1165
+ return out;
1166
+ }
1167
+ if (term.name === ',' && term.arity === 2) {
1168
+ forwardRuleDependencies(term.args[0], out);
1169
+ forwardRuleDependencies(term.args[1], out);
1170
+ }
1171
+ return out;
1172
+ }
1173
+
1174
+ // Autoloading needs a slightly richer dependency walk than recursion analysis.
1175
+ // Meta-predicates such as forall/2 and setof/3 are themselves callable
1176
+ // procedures while also containing executable goals. The recursion graph
1177
+ // intentionally looks through those wrappers; the autoloader must see both the
1178
+ // wrapper and any statically visible nested calls so a bundled library can be
1179
+ // loaded before execution reaches it.
1180
+ function collectAutoloadGoalDependencies(goal, out = []) {
1181
+ if (goal?.type === ATOM) {
1182
+ out.push({ key: `${goal.name}/0`, name: goal.name, arity: 0, module: goal.module });
1183
+ return out;
1184
+ }
1185
+ if (goal?.type !== COMPOUND) return out;
1186
+ if (goal.name === ',' && goal.arity === 2) {
1187
+ collectAutoloadGoalDependencies(goal.args[0], out);
1188
+ collectAutoloadGoalDependencies(goal.args[1], out);
1189
+ return out;
1190
+ }
1191
+ if ((goal.name === ';' || goal.name === '->') && goal.arity === 2) {
1192
+ collectAutoloadGoalDependencies(goal.args[0], out);
1193
+ collectAutoloadGoalDependencies(goal.args[1], out);
1194
+ return out;
1195
+ }
1196
+ if ((goal.name === '\\+' || goal.name === 'not' || goal.name === 'tnot' || goal.name === 'once') && goal.arity === 1) {
1197
+ collectAutoloadGoalDependencies(goal.args[0], out);
1198
+ return out;
1199
+ }
1200
+
1201
+ out.push({ key: `${goal.name}/${goal.arity}`, name: goal.name, arity: goal.arity, module: goal.module });
1202
+
1203
+ if (goal.name === 'forall' && goal.arity === 2) {
1204
+ collectAutoloadGoalDependencies(goal.args[0], out);
1205
+ collectAutoloadGoalDependencies(goal.args[1], out);
1206
+ } else if ((goal.name === 'findall' || goal.name === 'bagof' || goal.name === 'setof' || goal.name === 'sumall') && goal.arity === 3) {
1207
+ collectAutoloadGoalDependencies(goal.args[1], out);
1208
+ } else if (goal.name === 'countall' && goal.arity === 2) {
1209
+ collectAutoloadGoalDependencies(goal.args[0], out);
1210
+ } else if ((goal.name === 'aggregate_min' || goal.name === 'aggregate_max') && goal.arity === 5) {
1211
+ collectAutoloadGoalDependencies(goal.args[2], out);
1212
+ } else if (goal.name === 'catch' && goal.arity === 3) {
1213
+ collectAutoloadGoalDependencies(goal.args[0], out);
1214
+ collectAutoloadGoalDependencies(goal.args[2], out);
1215
+ } else if ((goal.name === 'call_cleanup' || goal.name === 'setup_call_cleanup') && (goal.arity === 2 || goal.arity === 3)) {
1216
+ for (const arg of goal.args) collectAutoloadGoalDependencies(arg, out);
1217
+ } else if (goal.name === 'call' && goal.arity === 1) {
1218
+ collectAutoloadGoalDependencies(goal.args[0], out);
1219
+ }
1220
+ return out;
1221
+ }
1222
+
1223
+ function forwardRuleAutoloadDependencies(term, out = []) {
1224
+ if (term?.type !== COMPOUND) return out;
1225
+ if (term.name === ':+' && term.arity === 2) {
1226
+ collectAutoloadGoalDependencies(term.args[1], out);
1227
+ forwardRuleAutoloadDependencies(term.args[0], out);
1228
+ return out;
1229
+ }
1230
+ if (term.name === ',' && term.arity === 2) {
1231
+ forwardRuleAutoloadDependencies(term.args[0], out);
1232
+ forwardRuleAutoloadDependencies(term.args[1], out);
1233
+ }
1234
+ return out;
1235
+ }
1236
+
1237
+ function groupAutoloadDependencies(group) {
1238
+ const dependencies = [];
1239
+ for (const clause of group.clauses) {
1240
+ if (group.name === ':+') dependencies.push(...forwardRuleAutoloadDependencies(clause.head));
1241
+ if (isCompactBinaryClause(clause)) {
1242
+ if (clause.bodyName != null) {
1243
+ dependencies.push({
1244
+ key: `${clause.bodyName}/2`,
1245
+ name: clause.bodyName,
1246
+ arity: 2,
1247
+ module: clause.module ?? group.module,
1248
+ });
1249
+ }
1250
+ continue;
1251
+ }
1252
+ for (const goal of clause.body) collectAutoloadGoalDependencies(goal, dependencies);
1253
+ }
1254
+ return dependencies;
1255
+ }
1256
+
1152
1257
  function parseInteropGoalInputs(inputs, options, program) {
1153
1258
  if (inputs == null) return [];
1154
1259
  const values = Array.isArray(inputs) ? inputs : [inputs];
@@ -1164,7 +1269,7 @@ function parseInteropGoalInputs(inputs, options, program) {
1164
1269
 
1165
1270
  function extraGoalDependencies(goals) {
1166
1271
  const dependencies = [];
1167
- for (const goal of goals) dependencies.push(...collectGoalDependencies(goal, false, true));
1272
+ for (const goal of goals) collectAutoloadGoalDependencies(goal, dependencies);
1168
1273
  return dependencies;
1169
1274
  }
1170
1275
 
@@ -1190,7 +1295,7 @@ function libraryAutoloadRequests(program, extraGoals = []) {
1190
1295
  const requests = new Map();
1191
1296
  for (const group of program.groups.values()) {
1192
1297
  if (bundledLibraryModule(program, group.module)) continue;
1193
- for (const dependency of groupDependencies(group)) {
1298
+ for (const dependency of groupAutoloadDependencies(group)) {
1194
1299
  const targetModule = dependency.module ?? group.module;
1195
1300
  if (procedureResolvedBeforeAutoload(program, dependency, targetModule)) continue;
1196
1301
  const library = autoloadLibraryFor(dependency);
@@ -1206,7 +1311,7 @@ function libraryAutoloadRequests(program, extraGoals = []) {
1206
1311
  }
1207
1312
  }
1208
1313
  for (const goal of program.initializations) {
1209
- for (const dependency of collectGoalDependencies(goal, false, true)) {
1314
+ for (const dependency of collectAutoloadGoalDependencies(goal)) {
1210
1315
  const targetModule = dependency.module ?? 'user';
1211
1316
  if (procedureResolvedBeforeAutoload(program, dependency, targetModule)) continue;
1212
1317
  const library = autoloadLibraryFor(dependency);
@@ -28,6 +28,7 @@ const moduleFiles = Object.freeze({
28
28
  debug: 'debug.pl',
29
29
  dif: 'dif.pl',
30
30
  error: 'error.pl',
31
+ eyelet: 'eyelet.pl',
31
32
  format: 'format.pl',
32
33
  freeze: 'freeze.pl',
33
34
  gensym: 'gensym.pl',
@@ -102,6 +103,7 @@ export const eyePrologPortableLibraryIndicators = Object.freeze([
102
103
  'sat/1', 'taut/2', 'labeling/1', 'sat_count/2', 'random_labeling/2', 'weighted_maximum/3',
103
104
  'lt/2', 'gt/2', 'le/2', 'ge/2',
104
105
  'difference/3',
106
+ 'stable/1', 'becomes/2',
105
107
  'seq/3', 'seqq/3',
106
108
  'debug/1', 'debug/3', 'nodebug/1', 'bb_get/2', 'bb_put/2', 'bb_b_put/2', 'bb_global_get/2',
107
109
  '*/1', '$/1', '$-/1',
@@ -4807,8 +4807,10 @@ ${profile}`;
4807
4807
  assertIncludes(book, 'Autoloading therefore supplies', 'autoload syntax boundary');
4808
4808
  assertIncludes(book, 'Residual constraints are part of the displayed answer even when', 'top-level hidden residuals');
4809
4809
  assertIncludes(book, 'EyeProlog normal mode also accepts `:+`', 'native forward-rule extension');
4810
+ assertIncludes(book, '`library(eyelet)`', 'Eyelet library surface');
4811
+ assertIncludes(readme, '`library(eyelet)`', 'README Eyelet library surface');
4810
4812
  assertIncludes(profile, 'an unresolved unqualified predicate may autoload its unique provider', 'Why EyeProlog autoload policy');
4811
- assertIncludes(readme, 'Bundled `src/lib/` predicates autoload in files, CLI/API goals, and the REPL', 'README autoload summary');
4813
+ assertIncludes(readme, 'Bundled `src/lib/` predicates autoload in files, CLI/API goals,', 'README autoload summary');
4812
4814
  },
4813
4815
  },
4814
4816
  {
@@ -5469,6 +5471,20 @@ function apiCases() {
5469
5471
  assertEqual(result.haltCode, null, 'forward halt code');
5470
5472
  },
5471
5473
  },
5474
+ {
5475
+ name: 'native forward rules autoload library(eyelet) helpers from :+ premises',
5476
+ run: () => {
5477
+ const result = run(`
5478
+ :- dynamic(state/1).
5479
+ state(a).
5480
+ changed :+ becomes(state(a), state(b)).
5481
+ seed :+ state(b).
5482
+ ready :+ seed, stable(1).
5483
+ true :+ ready.
5484
+ `);
5485
+ assertEqual(result.stdout, 'ready.\n', 'forward helper stdout');
5486
+ },
5487
+ },
5472
5488
  {
5473
5489
  name: 'run exposes false/0 as an always-failing built-in',
5474
5490
  run: () => {
@@ -6287,7 +6303,7 @@ answer(ok) :-
6287
6303
  assertEqual(registry.get('setup_call_cleanup', 3), null, 'setup_call_cleanup/3 is absent from the ISO registry');
6288
6304
  assertEqual(Boolean(library.get('setup_call_cleanup', 3)), true, 'setup_call_cleanup/3 is an EyeProlog cleanup control');
6289
6305
  assertEqual(registeredNativeEyePrologLibraryNames().length, 58, 'public host-supported EyeProlog library count');
6290
- assertEqual(eyePrologPortableLibraryIndicators.length, 241, 'portable Prolog library count');
6306
+ assertEqual(eyePrologPortableLibraryIndicators.length, 243, 'portable Prolog library count');
6291
6307
  assertEqual(eyePrologInteropLibraryIndicators.length, 189, 'cross-implementation interop profile count');
6292
6308
  assertEqual(eyePrologInteropLibraryModules.length, 23, 'common explicit library module profile count');
6293
6309
  assertEqual(eyePrologInteropAutoload['member/2'], 'lists', 'member/2 canonical autoload');
@@ -6299,11 +6315,13 @@ answer(ok) :-
6299
6315
  assertEqual(eyePrologInteropAutoload['set_nth0/4'] ?? null, null, 'set_nth0/4 is outside the conservative interop subset');
6300
6316
  assertEqual(eyePrologLibraryAutoload['set_nth0/4'], 'lists', 'complete library autoload includes EyeProlog-only exports');
6301
6317
  assertEqual(eyePrologLibraryAutoload['pairs_keys_values/3'], 'pairs', 'complete library autoload includes library(pairs)');
6302
- assertEqual(eyePrologLibraryAutoloadModules.length, 35, 'all bundled src/lib modules are indexed for autoload');
6318
+ assertEqual(eyePrologLibraryAutoload['stable/1'], 'eyelet', 'complete library autoload includes Eyelet stable/1');
6319
+ assertEqual(eyePrologLibraryAutoload['becomes/2'], 'eyelet', 'complete library autoload includes Eyelet becomes/2');
6320
+ assertEqual(eyePrologLibraryAutoloadModules.length, 36, 'all bundled src/lib modules are indexed for autoload');
6303
6321
  assertEqual(eyePrologNativeLibraryIndicators.length, 58, 'host-supported library count');
6304
6322
  assertEqual(eyePrologNativeLibraryIndicators.slice(0, 3).join(','), 'call_nth/2,freeze/2,dif/2', 'control and constraint predicates requiring host support');
6305
6323
  assertEqual(eyePrologNativeLibraryIndicators.includes('random/1'), true, 'stateful random/1 is classified as host-supported');
6306
- assertEqual(eyePrologLibraryIndicators.length, 299, 'complete non-ISO EyeProlog library surface');
6324
+ assertEqual(eyePrologLibraryIndicators.length, 301, 'complete non-ISO EyeProlog library surface');
6307
6325
  assertEqual(registry.get('eyeprolog__call_nth', 2), null, 'private call_nth adapter is absent from ISO registry');
6308
6326
  assertEqual(Boolean(library.get('eyeprolog__call_nth', 2)), true, 'private call_nth adapter is registered for EyeProlog');
6309
6327
  assertEqual(Boolean(library.get('eyeprolog__call_residue_vars', 2)), true, 'private call_residue_vars adapter is registered for EyeProlog');
@@ -1681,16 +1681,16 @@ is a forward rule. When a loaded program contains such rules and no explicit
1681
1681
  top-level goal overrides them, the engine runs a native closure driver rather
1682
1682
  than a Prolog meta-interpreter. It repeatedly solves premises against the
1683
1683
  current program, asserts novel conjuncts from successful conclusions, and
1684
- continues until no new conclusion is added. This is the execution path used by
1685
- the Eyelet adapter.
1684
+ continues until no new conclusion is added. This is the execution path used when
1685
+ Eyelet sources are run directly by EyeProlog.
1686
1686
 
1687
1687
  Two conclusions have control meaning. `true :+ Goal` is a query and prints each
1688
1688
  distinct successful instance of `Goal`. `false :+ Goal` is an integrity fuse:
1689
1689
  on success EyeProlog prints `fuse(Goal)` and returns halt status 2. Variables
1690
1690
  that occur only in an ordinary derived conclusion are existential and become
1691
1691
  `sk_0`, `sk_1`, and so on; a derived conclusion that is itself a `:+` rule keeps
1692
- its variables universal. Eyelet's `stable/1` and `becomes/2` remain library-level
1693
- compatibility helpers, while closure selection and assertion are native.
1692
+ its variables universal. The bundled `library(eyelet)` exports the `:+` operator together with
1693
+ `stable/1` and `becomes/2`, while closure selection and assertion are native.
1694
1694
 
1695
1695
  The JavaScript convenience `run()` function selects this forward mode when no
1696
1696
  explicit `goal` or `goals` option is supplied. Advanced embedders can inspect a
@@ -6278,12 +6278,12 @@ so side effects occur in Prolog execution order.
6278
6278
 
6279
6279
  ### The EyeProlog library
6280
6280
 
6281
- EyeProlog exposes **299 distinct non-ISO library and normal-extension predicate
6281
+ EyeProlog exposes **301 distinct non-ISO library and normal-extension predicate
6282
6282
  indicators** in addition to the 129 indicators in its isolated ISO profile.
6283
- **241 are defined entirely as ordinary Prolog clauses** in focused modules under
6283
+ **243 are defined entirely as ordinary Prolog clauses** in focused modules under
6284
6284
  `src/lib/`; **58 use host support** for control, attributed variables,
6285
6285
  constraints, or observability. The ISO and library catalogs therefore cover
6286
- **428 distinct predicate indicators**. Normal-mode controls and observability
6286
+ **430 distinct predicate indicators**. Normal-mode controls and observability
6287
6287
  relations that do not belong to a library, such as `call_cleanup/2`,
6288
6288
  `setup_call_cleanup/3`, `statistics/0,2`, and `tnot/1`, are documented in their
6289
6289
  own sections rather than counted as library predicates. `time/1` is additionally
@@ -6296,7 +6296,7 @@ The sources are `src/lib/aggregate.pl`, `src/lib/arithmetic.pl`,
6296
6296
  `src/lib/charsio.pl`, `src/lib/clpb.pl`, `src/lib/clpz.pl`,
6297
6297
  `src/lib/comparison.pl`, `src/lib/dates.pl`, `src/lib/dcgs.pl`,
6298
6298
  `src/lib/debug.pl`, `src/lib/dif.pl`, `src/lib/error.pl`,
6299
- `src/lib/format.pl`, `src/lib/freeze.pl`, `src/lib/gensym.pl`,
6299
+ `src/lib/eyelet.pl`, `src/lib/format.pl`, `src/lib/freeze.pl`, `src/lib/gensym.pl`,
6300
6300
  `src/lib/iso_ext.pl`, `src/lib/lambda.pl`, `src/lib/lists.pl`,
6301
6301
  `src/lib/ordsets.pl`, `src/lib/pairs.pl`, `src/lib/pio.pl`,
6302
6302
  `src/lib/primes.pl`, `src/lib/prologue.pl`, `src/lib/random.pl`,
@@ -6368,6 +6368,7 @@ bindings make one aligned subterm pair sufficient. For example:
6368
6368
  | `library(debug)` | `*/1`, `$/1`, `$-/1`, `debug/1`, `debug/3`, `nodebug/1`, `bb_get/2`, `bb_put/2`, `bb_b_put/2`, `bb_global_get/2` | Declarative debug operators and constraint-library blackboards |
6369
6369
  | `library(dif)` | `dif/2` | Common module facade over native delayed disequality |
6370
6370
  | `library(error)` | `must_be/2`, `can_be/2`, `instantiation_error/0`, `instantiation_error/1`, `domain_error/2`, `domain_error/3`, `type_error/2`, `type_error/3`, `representation_error/1`, `resource_error/1`, `call_with_error_context/2` | Error checking and construction |
6371
+ | `library(eyelet)` | `stable/1`, `becomes/2` | Eyelet forward-reasoning state helpers; the `:+` operator is exported by the module and executed natively by EyeProlog |
6371
6372
  | `library(format)` | `format_/4`, `format/2`, `format/3`, `listing/1`, `portray_clause_/3`, `portray_clause/1`, `portray_clause/2` | Formatted DCG text and output; `format_/4` and `portray_clause_/3` are the expanded nonterminals |
6372
6373
  | `library(freeze)` | `freeze/2` | Delayed goals |
6373
6374
  | `library(gensym)` | `gensym/2`, `reset_gensym/1` | Process-local generated atoms |
package/why-eyeprolog.md CHANGED
@@ -143,8 +143,9 @@ This avoids a heuristic silently changing answer multiplicity, termination, or
143
143
  resource behavior for an otherwise ordinary recursive predicate.
144
144
 
145
145
  Forward chaining is explicit too. Normal mode recognizes `Conclusion :+ Premise`
146
- and executes those rules with a native fixed-point driver, including Eyelet's
147
- `true :+ Goal` query and `false :+ Goal` fuse conventions. Strict ISO mode
146
+ and executes those rules with a native fixed-point driver. The bundled
147
+ `library(eyelet)` exports the `:+` operator, `stable/1`, and `becomes/2`, including
148
+ Eyelet's `true :+ Goal` query and `false :+ Goal` fuse conventions. Strict ISO mode
148
149
  removes the operator. Because resource exhaustion is not logical failure, normal
149
150
  execution has no hidden depth cutoff; an explicitly requested depth limit raises
150
151
  `resource_error(depth_limit)`.