eyeprolog 1.3.3 → 1.3.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
@@ -143,12 +143,20 @@ Trealla/Scryer organization for predicates such as `member/2`, `memberchk/2`,
143
143
  `length(Xs, N)` enumerates lists of increasing length together with `N = 0, 1,
144
144
  2, ...`.
145
145
 
146
+ `library(iso_ext)` is also accepted as a common interop module name.
147
+ EyeProlog exports `call_nth/2` there, so Scryer-style source can explicitly use
148
+ `:- use_module(library(iso_ext)).`; unqualified source may still autoload it.
149
+ The aligned `library(lists)` and `library(iso_ext)` exports are kept disjoint so
150
+ they can be imported together without an accidental import conflict. EyeProlog's
151
+ legacy `library(prologue)` remains a compatibility umbrella and should be
152
+ selectively imported when mixed with the aligned modules.
153
+
146
154
  Outside `--iso-strict`, an otherwise undefined unqualified call may autoload a
147
155
  predicate only when the interop profile has one canonical EyeProlog provider.
148
- For example, `member/2` autoloads from `library(lists)`, while `between/3` and
149
- `call_nth/2` can use EyeProlog's internal Prologue implementation without
150
- requiring portable source to name `library(prologue)`. Use `--no-autoload` to
151
- disable this convenience. Strict ISO mode never autoloads library predicates.
156
+ For example, `member/2` autoloads from `library(lists)`, `call_nth/2` from
157
+ `library(iso_ext)`, and `between/3` from EyeProlog's internal
158
+ `library(prologue)` implementation. Use `--no-autoload` to disable this
159
+ convenience. Strict ISO mode never autoloads library predicates.
152
160
 
153
161
  Use `-w` / `--warnings` to diagnose dependencies outside the interop profile,
154
162
  or `--portable` to make such diagnostics fail the run. This catches both
@@ -1,6 +1,7 @@
1
1
  % From The Art of EyeProlog, Chapter 8.
2
2
  :- use_module(library(aggregate)).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  findall(Template, Goal, List).
6
7
  countall(Goal, Count).
@@ -1,6 +1,7 @@
1
1
  % From The Art of EyeProlog, Chapter 39.
2
2
  :- use_module(library(aggregate)).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  cost(a, 8).
6
7
  cost(b, 3).
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % A tiny automatically tabled chart parser for a context-free grammar.
4
5
  %
package/examples/dog.pl CHANGED
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % Dog-license compliance rule adapted from Eyeling dog.n3.
4
5
  %
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % A pandigital cryptarithm: DONALD + GERALD = ROBERT.
4
5
  %
@@ -1,6 +1,7 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(prologue), [between/3]).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  % Bounded equality saturation over tiny arithmetic expression terms.
6
7
  %
@@ -1,6 +1,7 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(prologue), [between/3]).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  % Tiny job-shop scheduling benchmark.
6
7
  %
@@ -1,5 +1,6 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(lists)).
3
+ :- use_module(library(iso_ext)).
3
4
 
4
5
  % 0/1 knapsack optimization with aggregate_max/5.
5
6
  %
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % Bounded Knuth-Bendix-style completion for append/2 terms.
4
5
  %
@@ -1,6 +1,7 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(prologue), [between/3]).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  % Matrix-chain multiplication order by automatically tabled interval dynamic programming.
6
7
  %
@@ -1,5 +1,6 @@
1
1
  :- use_module(library(prologue), [between/3]).
2
2
  :- use_module(library(lists)).
3
+ :- use_module(library(iso_ext)).
3
4
 
4
5
  % Missionaries-and-cannibals river crossing as guarded state-space search.
5
6
  %
@@ -1,5 +1,6 @@
1
1
  :- use_module(library(prologue), [between/3]).
2
2
  :- use_module(library(lists)).
3
+ :- use_module(library(iso_ext)).
3
4
 
4
5
  % Prime ranges and Euler totient over finite integer domains.
5
6
  %
@@ -1,5 +1,6 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(lists)).
3
+ :- use_module(library(iso_ext)).
3
4
 
4
5
  % Register allocation as bounded graph coloring with spilling.
5
6
  %
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % Cryptarithm search for SEND + MORE = MONEY.
4
5
  %
@@ -1,4 +1,5 @@
1
1
  :- use_module(library(lists)).
2
+ :- use_module(library(iso_ext)).
2
3
 
3
4
  % Stable-marriage search with explicit blocking-pair detection.
4
5
  %
@@ -1,6 +1,7 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(prologue), [between/3]).
3
3
  :- use_module(library(lists)).
4
+ :- use_module(library(iso_ext)).
4
5
 
5
6
  % Euler totients and coprimality by automatically tabled Euclidean gcd.
6
7
  %
@@ -1,5 +1,6 @@
1
1
  :- use_module(library(aggregate)).
2
2
  :- use_module(library(lists)).
3
+ :- use_module(library(iso_ext)).
3
4
 
4
5
  % Weighted interval scheduling via automatically tabled dynamic programming.
5
6
  %
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.3",
6
+ "version": "1.3.4",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -2032,11 +2032,28 @@ function prologErrorBall(error) {
2032
2032
  return freshCopy(term, new Env());
2033
2033
  }
2034
2034
  function* catchBuiltin({ solver, goal, env }) {
2035
- const child = solver.cloneForInnerGoal();
2035
+ let child = null;
2036
2036
  try {
2037
2037
  // Corrigendum 2 removed catch/3's own callability errors so that errors
2038
2038
  // raised while converting/executing the protected goal are catchable.
2039
- yield* child.solve([callable(goal.args[0], env)], env.clone(), 0);
2039
+ const invoked = callable(goal.args[0], env);
2040
+ const direct = solver.registry.get(invoked.name, invoked.arity);
2041
+ if (direct?.deterministic && (direct.shouldUse == null || direct.shouldUse({ solver, goal: invoked, env }))) {
2042
+ // A deterministic builtin has no choice points whose lifetime must be
2043
+ // isolated in a child solver. Running it directly avoids constructing a
2044
+ // complete Solver for hot caught failures such as number_chars/2 syntax
2045
+ // probes, while the cloned environment keeps catch/3's rollback boundary.
2046
+ const iterator = direct.handler({ solver, goal: invoked, env: env.clone() });
2047
+ const result = iterator.next();
2048
+ if (result.done) solver.stats.deterministic_builtin_failures++;
2049
+ else {
2050
+ solver.stats.deterministic_builtin_successes++;
2051
+ yield result.value;
2052
+ }
2053
+ return;
2054
+ }
2055
+ child = solver.cloneForInnerGoal();
2056
+ yield* child.solve([invoked], env.clone(), 0);
2040
2057
  } catch (error) {
2041
2058
  const ball = error instanceof ThrownTerm
2042
2059
  ? error.term
@@ -2046,9 +2063,15 @@ function* catchBuiltin({ solver, goal, env }) {
2046
2063
  if (ball == null) throw error;
2047
2064
  const recovered = env.clone();
2048
2065
  if (!unify(goal.args[1], ball, recovered)) throw error;
2049
- yield* solver.solve([callable(goal.args[2], recovered)], recovered, 0);
2066
+ const recovery = callable(goal.args[2], recovered);
2067
+ if (recovery.type === ATOM && (recovery.name === 'false' || recovery.name === 'fail')) return;
2068
+ if (recovery.type === ATOM && recovery.name === 'true') {
2069
+ yield recovered;
2070
+ return;
2071
+ }
2072
+ yield* solver.solve([recovery], recovered, 0);
2050
2073
  } finally {
2051
- solver.absorbStatsFrom(child);
2074
+ if (child != null) solver.absorbStatsFrom(child);
2052
2075
  }
2053
2076
  }
2054
2077
  function* throwBuiltin({ goal, env }) {
@@ -2071,7 +2094,20 @@ function* repeatBuiltin({ env }) {
2071
2094
  while (true) yield env;
2072
2095
  }
2073
2096
  function* negationBuiltin({ solver, goal, env }) {
2074
- for (const _ of solver.cloneForInnerGoal(1).solve([callable(goal.args[0], env)], env.clone(), 0)) return;
2097
+ const invoked = callable(goal.args[0], env);
2098
+ const direct = solver.registry.get(invoked.name, invoked.arity);
2099
+ if (direct?.deterministic && (direct.shouldUse == null || direct.shouldUse({ solver, goal: invoked, env }))) {
2100
+ const iterator = direct.handler({ solver, goal: invoked, env: env.clone() });
2101
+ const result = iterator.next();
2102
+ if (result.done) {
2103
+ solver.stats.deterministic_builtin_failures++;
2104
+ yield env;
2105
+ } else {
2106
+ solver.stats.deterministic_builtin_successes++;
2107
+ }
2108
+ return;
2109
+ }
2110
+ for (const _ of solver.cloneForInnerGoal(1).solve([invoked], env.clone(), 0)) return;
2075
2111
  yield env;
2076
2112
  }
2077
2113
  function* solveControlBranch(solver, goal, env) {
@@ -1,6 +1,7 @@
1
1
  /** Widely implemented extensions to the ISO Prolog core. */
2
2
 
3
3
  :- module(iso_ext, [
4
+ call_nth/2,
4
5
  countall/2,
5
6
  forall/2,
6
7
  succ/2,
@@ -9,6 +10,7 @@
9
10
  variant/2
10
11
  ]).
11
12
 
13
+ :- meta_predicate(call_nth(0, '?')).
12
14
  :- meta_predicate(countall(0, '?')).
13
15
  :- meta_predicate(forall(0, 0)).
14
16
  :- meta_predicate(findall('?', 0, '?', '?')).
@@ -17,6 +19,8 @@
17
19
  % Trealla. These definitions use only EyeProlog's ISO profile; extensions that
18
20
  % require runtime cleanup, choice-point, alarm, or timeout hooks are omitted.
19
21
 
22
+ call_nth(Goal, Nth) :- eyeprolog__call_nth(Goal, Nth).
23
+
20
24
  countall(Goal, Count) :- eyeprolog__countall(Goal, Count).
21
25
 
22
26
  forall(Condition, Action) :-
package/src/lib/lists.pl CHANGED
@@ -28,7 +28,6 @@
28
28
  min_list/2,
29
29
  max_list/2,
30
30
  list_to_set/2,
31
- countall/2,
32
31
  set_nth0/4,
33
32
  take/3,
34
33
  drop/3,
@@ -45,7 +44,6 @@
45
44
  :- meta_predicate(foldl(3, '?', '?', '?')).
46
45
  :- meta_predicate(foldl(4, '?', '?', '?', '?')).
47
46
  :- meta_predicate(foldl(5, '?', '?', '?', '?', '?')).
48
- :- meta_predicate(countall(0, '?')).
49
47
 
50
48
  % Common pure-Prolog library predicates for EyeProlog.
51
49
  %
@@ -168,7 +166,6 @@ max_list([X|Xs], Max) :- lists__max_list(Xs, X, Max).
168
166
 
169
167
  list_to_set(List, Set) :- lists__list_to_set(List, [], Set).
170
168
 
171
- countall(Goal, Count) :- eyeprolog__countall(Goal, Count).
172
169
 
173
170
  set_nth0(0, [_|Xs], X, [X|Xs]).
174
171
  set_nth0(N, [Y|Ys], X, [Y|Zs]) :-
package/src/solver.js CHANGED
@@ -374,7 +374,24 @@ export class Solver {
374
374
  }
375
375
  qualifyMetaArguments(goal, group);
376
376
 
377
- const lengthIterator = prologueLengthIterator(this, group, goal, env);
377
+ const memberIterator = bundledMemberIterator(this, group, goal, env);
378
+ if (memberIterator != null) {
379
+ const firstResult = memberIterator.next();
380
+ if (firstResult.done) break;
381
+ stack.push({
382
+ kind: 'resumeBuiltin',
383
+ iterator: memberIterator,
384
+ goals: rest,
385
+ depth: depth + 1,
386
+ active,
387
+ });
388
+ goals = rest;
389
+ env = firstResult.value;
390
+ depth++;
391
+ continue;
392
+ }
393
+
394
+ const lengthIterator = bundledLengthIterator(this, group, goal, env);
378
395
  if (lengthIterator != null) {
379
396
  const firstResult = lengthIterator.next();
380
397
  if (firstResult.done) break;
@@ -757,9 +774,36 @@ function groupNeedsActiveFrame(group) {
757
774
  return group.cutReachable !== false || (group.recursive && !group.linearNumeric);
758
775
  }
759
776
 
760
- function prologueLengthIterator(solver, group, goal, env) {
777
+ function bundledMemberIterator(solver, group, goal, env) {
778
+ if (solver.registry.eyePrologLibrary !== true ||
779
+ !['lists', 'prologue'].includes(group.module) || group.name !== 'member' || group.arity !== 2 ||
780
+ group.bundledLibrary !== true || group.clauses.length !== 2) {
781
+ return null;
782
+ }
783
+
784
+ // Keep the relational/open-list cases in ordinary Prolog. When the second
785
+ // argument is already a finite list, however, the canonical two-clause
786
+ // member/2 definition is exactly a left-to-right scan and can avoid clause
787
+ // freshening and recursive solver frames for every element.
788
+ let cursor = deref(goal.args[1], env);
789
+ while (isCons(cursor)) cursor = deref(cursor.args[1], env);
790
+ if (!isEmptyList(cursor)) return null;
791
+ return bundledMemberSolutions(solver, goal, env);
792
+ }
793
+
794
+ function* bundledMemberSolutions(solver, goal, env) {
795
+ let cursor = deref(goal.args[1], env);
796
+ while (isCons(cursor)) {
797
+ const next = env.clone();
798
+ solver.stats.unify_calls++;
799
+ if (unify(goal.args[0], cursor.args[0], next)) yield next;
800
+ cursor = deref(cursor.args[1], env);
801
+ }
802
+ }
803
+
804
+ function bundledLengthIterator(solver, group, goal, env) {
761
805
  if (solver.registry.eyePrologLibrary !== true ||
762
- group.module !== 'prologue' || group.name !== 'length' || group.arity !== 2 ||
806
+ !['lists', 'prologue'].includes(group.module) || group.name !== 'length' || group.arity !== 2 ||
763
807
  group.bundledLibrary !== true || group.clauses.length !== 2) {
764
808
  return null;
765
809
  }
@@ -778,10 +822,10 @@ function prologueLengthIterator(solver, group, goal, env) {
778
822
  if (env._delays?.has(cursor.name)) return null;
779
823
  if (length.type === VAR && cursor.name === length.name) return null;
780
824
  }
781
- return prologueLengthSolutions(solver, goal, env);
825
+ return bundledLengthSolutions(solver, goal, env);
782
826
  }
783
827
 
784
- function* prologueLengthSolutions(solver, goal, env) {
828
+ function* bundledLengthSolutions(solver, goal, env) {
785
829
  const requestedLength = deref(goal.args[1], env);
786
830
  if (requestedLength.type !== VAR) {
787
831
  if (requestedLength.type !== NUMBER || !isDecimalInteger(requestedLength.name)) {
@@ -109,10 +109,10 @@ export const eyePrologInteropAutoload = Object.freeze({
109
109
  'foldl/6': 'lists',
110
110
  'sum_list/2': 'lists',
111
111
  'list_to_set/2': 'lists',
112
- // call_nth/2 is available in both Trealla and Scryer (the latter via
113
- // library(iso_ext)). EyeProlog keeps its adapter in library(prologue), so
114
- // autoloading hides that implementation-specific location from source.
115
- 'call_nth/2': 'prologue',
112
+ // call_nth/2 is available in both Trealla and Scryer. EyeProlog exposes
113
+ // it from library(iso_ext), matching the explicit Scryer import while still
114
+ // allowing Trealla-style unqualified source to use the same autoload entry.
115
+ 'call_nth/2': 'iso_ext',
116
116
  // Trealla and Scryer expose between/3 without an EyeProlog-style
117
117
  // library(prologue) dependency. EyeProlog keeps its implementation in the
118
118
  // Prologue module but autoloads it so portable source need not name that
@@ -125,9 +125,9 @@ export const eyePrologInteropLibraryIndicators = Object.freeze(
125
125
  );
126
126
 
127
127
  // Libraries whose *name* is part of the current interop profile. A program
128
- // may freely use_module(library(lists)); predicates in that module which are
129
- // outside eyePrologInteropLibraryIndicators are still diagnosed when used.
130
- export const eyePrologInteropLibraryModules = Object.freeze(['lists']);
128
+ // may freely use_module/1 with these common module names; predicates in those
129
+ // modules outside eyePrologInteropLibraryIndicators are still diagnosed when used.
130
+ export const eyePrologInteropLibraryModules = Object.freeze(['lists', 'iso_ext']);
131
131
 
132
132
  function runtimeStatistics(solver) {
133
133
  return { ...solver.stats, ...memoryStatistics() };
@@ -18,6 +18,7 @@ function withStandardModules(text) {
18
18
  return `:- use_module(library(aggregate)).
19
19
  :- use_module(library(comparison)).
20
20
  :- use_module(library(dates)).
21
+ :- use_module(library(iso_ext)).
21
22
  :- use_module(library(lists)).
22
23
  :- use_module(library(primes)).
23
24
  :- use_module(library(prologue), [between/3]).
@@ -20,6 +20,7 @@ import {
20
20
  createDefaultRegistry,
21
21
  getStrictIsoRegistry,
22
22
  getEyePrologRegistry,
23
+ standardLibrarySources,
23
24
  eyePrologLibraryIndicators,
24
25
  eyePrologNativeLibraryIndicators,
25
26
  eyePrologPortableLibraryIndicators,
@@ -67,6 +68,7 @@ function withStandardModules(source) {
67
68
  return `:- use_module(library(aggregate)).
68
69
  :- use_module(library(comparison)).
69
70
  :- use_module(library(dates)).
71
+ :- use_module(library(iso_ext)).
70
72
  :- use_module(library(lists)).
71
73
  :- use_module(library(primes)).
72
74
  :- use_module(library(prologue), [between/3]).
@@ -2214,6 +2216,22 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
2214
2216
  assertEqual(result.stderr, '', 'stderr');
2215
2217
  },
2216
2218
  },
2219
+ {
2220
+ name: 'library(lists) and library(iso_ext) co-import without collisions',
2221
+ run: () => {
2222
+ const input = [
2223
+ ':- use_module(library(lists)).',
2224
+ ':- use_module(library(iso_ext)).',
2225
+ '%% goal: answer(N)',
2226
+ 'answer(N) :- call_nth(member(_, [a,b,c]), N), N = 2.',
2227
+ '',
2228
+ ].join('\n');
2229
+ const result = runCli(['--portable', '--no-autoload', '-'], { input });
2230
+ assertEqual(result.status, 0, 'exit status');
2231
+ assertEqual(result.stdout, 'answer(2).\n', 'stdout');
2232
+ assertEqual(result.stderr, '', 'stderr');
2233
+ },
2234
+ },
2217
2235
  {
2218
2236
  name: 'strict ISO mode disables interop autoloading',
2219
2237
  run: () => {
@@ -3618,10 +3636,10 @@ check(A, B, C, D, E, F) :-
3618
3636
  assertEqual(registeredNativeEyePrologLibraryNames().length, 40, 'public native EyeProlog builtin count');
3619
3637
  assertEqual(eyePrologPortableLibraryIndicators.length, 62, 'portable Prolog library count');
3620
3638
  assertEqual(eyePrologInteropLibraryIndicators.length, 27, 'cross-implementation interop profile count');
3621
- assertEqual(eyePrologInteropLibraryModules.join(','), 'lists', 'common explicit library module profile');
3639
+ assertEqual(eyePrologInteropLibraryModules.join(','), 'lists,iso_ext', 'common explicit library module profile');
3622
3640
  assertEqual(eyePrologInteropAutoload['member/2'], 'lists', 'member/2 canonical autoload');
3623
3641
  assertEqual(eyePrologInteropAutoload['between/3'], 'prologue', 'between/3 canonical internal autoload');
3624
- assertEqual(eyePrologInteropAutoload['call_nth/2'], 'prologue', 'call_nth/2 canonical internal autoload');
3642
+ assertEqual(eyePrologInteropAutoload['call_nth/2'], 'iso_ext', 'call_nth/2 canonical interop autoload');
3625
3643
  assertEqual(eyePrologInteropAutoload['set_nth0/4'] ?? null, null, 'EyeProlog-only set_nth0/4 is not autoloadable');
3626
3644
  assertEqual(eyePrologNativeLibraryIndicators.length, 40, 'native host library count');
3627
3645
  assertEqual(eyePrologNativeLibraryIndicators.slice(0, 2).join(','), 'call_nth/2,freeze/2', 'control predicates requiring host support');
@@ -3671,6 +3689,28 @@ check(A, B, C, D, E, F) :-
3671
3689
  assertEqual(program.findGroup('random', 3)?.module, 'random', 'random/3 is imported from library(random)');
3672
3690
  },
3673
3691
  },
3692
+ {
3693
+ name: 'aligned bundled libraries have no overlapping full-module exports',
3694
+ run: () => {
3695
+ const owners = new Map();
3696
+ for (const [moduleName, entry] of standardLibrarySources) {
3697
+ // library(prologue) is the documented legacy compatibility umbrella;
3698
+ // aligned libraries must remain pairwise collision-free so full
3699
+ // use_module/1 imports can be combined safely.
3700
+ if (moduleName === 'prologue') continue;
3701
+ const parsed = Program.parse(entry.source);
3702
+ const definition = parsed.modules.get(moduleName);
3703
+ if (!definition) throw new Error(`missing module declaration for ${moduleName}`);
3704
+ for (const indicator of definition.exports.keys()) {
3705
+ const previous = owners.get(indicator);
3706
+ if (previous != null) throw new Error(`duplicate export ${indicator}: ${previous}, ${moduleName}`);
3707
+ owners.set(indicator, moduleName);
3708
+ }
3709
+ }
3710
+ assertEqual(owners.get('countall/2'), 'iso_ext', 'countall/2 has one aligned owner');
3711
+ assertEqual(owners.get('call_nth/2'), 'iso_ext', 'call_nth/2 has one aligned owner');
3712
+ },
3713
+ },
3674
3714
  {
3675
3715
  name: 'portable library executes against the ISO-only registry',
3676
3716
  run: () => {
@@ -1155,6 +1155,7 @@ Finite aggregation asks about a solution set:
1155
1155
  ```eyeprolog
1156
1156
  :- use_module(library(aggregate)).
1157
1157
  :- use_module(library(lists)).
1158
+ :- use_module(library(iso_ext)).
1158
1159
 
1159
1160
  findall(Template, Goal, List).
1160
1161
  countall(Goal, Count).
@@ -6019,8 +6020,8 @@ between solution branches.
6019
6020
  | `library(clpz)` | `#>/2`, `#</2`, `#>=/2`, `#=</2`, `#=/2`, `#\=/2`, `#\/1`, `#<==>/2`, `#==>/2`, `#<==/2`, `#\//2`, `#\/2`, `#/\/2`, `in/2`, `ins/2`, `all_different/1`, `all_distinct/1`, `nvalue/2`, `sum/3`, `scalar_product/4`, `tuples_in/2`, `labeling/2`, `label/1`, `indomain/1`, `lex_chain/1`, `serialized/2`, `global_cardinality/2`, `global_cardinality/3`, `circuit/1`, `chain/2`, `element/3`, `zcompare/3`, `fd_var/1`, `fd_inf/2`, `fd_sup/2`, `fd_size/2`, `fd_dom/2` |
6020
6021
  | `library(comparison)` | `lt/2`, `gt/2`, `le/2`, `ge/2` |
6021
6022
  | `library(dates)` | `difference/3` |
6022
- | `library(iso_ext)` | `countall/2`, `forall/2`, `succ/2`, `cfor/3`, `findall/4`, `variant/2` |
6023
- | `library(lists)` | `member/2`, `memberchk/2`, `select/3`, `append/2`, `append/3`, `last/2`, `same_length/2`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `reverse/2`, `length/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `foldl/4`, `foldl/5`, `foldl/6`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `countall/2`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4` |
6023
+ | `library(iso_ext)` | `call_nth/2`, `countall/2`, `forall/2`, `succ/2`, `cfor/3`, `findall/4`, `variant/2` |
6024
+ | `library(lists)` | `member/2`, `memberchk/2`, `select/3`, `append/2`, `append/3`, `last/2`, `same_length/2`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `reverse/2`, `length/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `foldl/4`, `foldl/5`, `foldl/6`, `sum_list/2`, `min_list/2`, `max_list/2`, `list_to_set/2`, `set_nth0/4`, `take/3`, `drop/3`, `slice/4` |
6024
6025
  | `library(primes)` | `smallest_divisor_from/3` |
6025
6026
  | `library(prologue)` | `member/2`, `append/3`, `length/2`, `between/3`, `select/3`, `succ/2`, `maplist/2`, `maplist/3`, `maplist/4`, `maplist/5`, `maplist/6`, `maplist/7`, `maplist/8`, `nth0/3`, `nth0/4`, `nth1/3`, `nth1/4`, `call_nth/2`, `freeze/2`, `foldl/4`, `foldl/5`, `foldl/6`, `countall/2` |
6026
6027
  | `library(random)` | `random/3` |
@@ -6046,14 +6047,21 @@ This generator mode is important for portable stress and enumeration programs.
6046
6047
  EyeProlog-only helpers such as `set_nth0/4`, `take/3`, `drop/3`, and `slice/4`
6047
6048
  remain available for compatibility but are outside this profile.
6048
6049
 
6050
+ `library(iso_ext)` is also recognized as a common interop module name.
6051
+ EyeProlog exports `call_nth/2` there, matching Scryer's explicit import
6052
+ organization while still permitting unqualified autoloaded source. The aligned
6053
+ `library(lists)` and `library(iso_ext)` export sets are intentionally disjoint,
6054
+ so a source file may import both without an accidental predicate collision.
6055
+ `library(prologue)` is retained as an EyeProlog compatibility umbrella and may
6056
+ overlap these modules; use selective imports when legacy code combines it with
6057
+ the aligned libraries.
6058
+
6049
6059
  Normal EyeProlog execution can autoload an otherwise undefined unqualified call
6050
6060
  only when the interop table assigns it one canonical provider. Thus `member/2`
6051
- autoloads from `library(lists)`. `between/3` and `call_nth/2` are also in the
6052
- interop profile; EyeProlog can satisfy them from its internal
6053
- `library(prologue)` implementation without forcing portable source to mention
6054
- that EyeProlog-specific library name. Autoloading is disabled by
6055
- `--no-autoload`, by the JavaScript option `autoload: false`, and always by
6056
- `--iso-strict`.
6061
+ autoloads from `library(lists)`, `call_nth/2` from `library(iso_ext)`, and
6062
+ `between/3` from the internal `library(prologue)` implementation. Autoloading is
6063
+ disabled by `--no-autoload`, by the JavaScript option `autoload: false`, and
6064
+ always by `--iso-strict`.
6057
6065
 
6058
6066
  `-w` / `--warnings` reports explicit non-profile library dependencies and calls
6059
6067
  to non-profile predicates from common libraries. `--portable` turns those
@@ -6075,11 +6083,14 @@ cumulative and two-dimensional scheduling constraints, and unbounded-domain
6075
6083
  propagation that are not yet exported here.
6076
6084
 
6077
6085
  `library(iso_ext)` collects extensions commonly found in mature Prolog
6078
- systems. `forall/2` checks an action for every solution of a condition;
6079
- `cfor/3` enumerates an inclusive evaluated integer range; `succ/2` relates
6080
- adjacent nonnegative integers; `findall/4` collects into a difference list;
6081
- and `variant/2` recognizes terms equal up to variable renaming. Its portable
6082
- `countall/2` counts solutions without exposing a template.
6086
+ systems. `call_nth/2` exposes the ordinal number of each solution and supports
6087
+ the explicit import used by Scryer-style source; `forall/2` checks an action for
6088
+ every solution of a condition; `cfor/3` enumerates an inclusive evaluated
6089
+ integer range; `succ/2` relates adjacent nonnegative integers; `findall/4`
6090
+ collects into a difference list; and `variant/2` recognizes terms equal up to
6091
+ variable renaming. Its portable `countall/2` counts solutions without exposing
6092
+ a template. `countall/2` is not exported by `library(lists)`, avoiding a
6093
+ full-module import collision between the two libraries.
6083
6094
 
6084
6095
  `uuid(+Seed0,-UUID,-Seed)` creates a version 4 UUID atom using `random/3`.
6085
6096
  Passing the returned seed to the next call produces the next UUID; restarting
@@ -6272,6 +6283,7 @@ There is no `not/1` alias; use ISO `\+/1`. `forall/2` is available from
6272
6283
  ```eyeprolog
6273
6284
  :- use_module(library(aggregate)).
6274
6285
  :- use_module(library(lists)).
6286
+ :- use_module(library(iso_ext)).
6275
6287
 
6276
6288
  cost(a, 8).
6277
6289
  cost(b, 3).