squint-cljs 0.12.193 → 0.13.195

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.
@@ -24,6 +24,8 @@ function dequal(foo, bar) {
24
24
  // supports primitives, Array, Set, Map and plain objects
25
25
  // like CLJS: does not support NaN
26
26
  if (foo === bar) return true;
27
+ // null and undefined are both nil in CLJS, so they compare equal
28
+ if (foo == null) return bar == null;
27
29
  var ctor, len, tmp;
28
30
 
29
31
  if (foo && bar && (ctor = foo.constructor) === bar.constructor) {
@@ -66,7 +68,9 @@ function dequal(foo, bar) {
66
68
  return true;
67
69
  }
68
70
 
69
- if (!ctor || typeof foo === 'object') {
71
+ // LazyIterable falls through to the sequential-equality path below; it is an
72
+ // object but must compare element-wise, not by enumerable properties
73
+ if ((!ctor || typeof foo === 'object') && !(foo instanceof LazyIterable)) {
70
74
  len = 0;
71
75
  for (const k in foo) {
72
76
  if (has.call(foo, k) && ++len && !has.call(bar, k)) return false;
@@ -75,6 +79,26 @@ function dequal(foo, bar) {
75
79
  return Object.keys(bar).length === len;
76
80
  }
77
81
  }
82
+
83
+ // Cross-type sequential equality, like CLJS `(= '(1 2) [1 2])`: vectors,
84
+ // lists and lazy seqs compare element-wise regardless of concrete type. Only
85
+ // reached when the same-constructor paths above did not apply, so equal-typed
86
+ // collections keep their fast paths.
87
+ if (
88
+ foo && bar &&
89
+ (Array.isArray(foo) || foo instanceof LazyIterable) &&
90
+ (Array.isArray(bar) || bar instanceof LazyIterable)
91
+ ) {
92
+ const fi = foo[Symbol.iterator]();
93
+ const bi = bar[Symbol.iterator]();
94
+ while (true) {
95
+ const a = fi.next();
96
+ const b = bi.next();
97
+ if (a.done || b.done) return !!(a.done && b.done);
98
+ if (!dequal(a.value, b.value)) return false;
99
+ }
100
+ }
101
+
78
102
  return false;
79
103
  }
80
104
  // end inlined version of dequals
@@ -179,16 +203,31 @@ export function assoc_BANG_(m, k, v, ...kvs) {
179
203
  return m;
180
204
  }
181
205
 
206
+ const _metaSym = Symbol('meta');
207
+
208
+ // Copies metadata from `from` onto `to` (when present) and returns `to`. Used
209
+ // to make value-producing operations (copy, empty, conj, into) carry metadata
210
+ // like Clojure does, instead of dropping it on the freshly built structure.
211
+ // Kept branch-light for the hot path: reads an absent symbol property (cheap,
212
+ // no hidden-class change) and only writes when metadata is actually present.
213
+ function copyMeta(from, to) {
214
+ const m = from?.[_metaSym];
215
+ if (m !== undefined) to[_metaSym] = m;
216
+ return to;
217
+ }
218
+
182
219
  function copy(o) {
183
220
  switch (typeConst(o)) {
184
221
  case MAP_TYPE:
185
- return new Map(o);
222
+ return copyMeta(o, new Map(o));
186
223
  case SET_TYPE:
187
- return new o.constructor(o);
224
+ return copyMeta(o, new o.constructor(o));
188
225
  case ARRAY_TYPE:
189
- return [...o];
226
+ return copyMeta(o, [...o]);
190
227
  case OBJECT_TYPE:
191
- return { ...o };
228
+ return copyMeta(o, { ...o });
229
+ case LIST_TYPE:
230
+ return copyMeta(o, new List(...o));
192
231
  default:
193
232
  throw new Error(`Don't know how to copy object of type ${typeof o}.`);
194
233
  }
@@ -234,6 +273,10 @@ function isObj(coll) {
234
273
  return coll.constructor === Object;
235
274
  }
236
275
 
276
+ function isVectorArray(x) {
277
+ return Array.isArray(x) && !(x instanceof List);
278
+ }
279
+
237
280
  export function object_QMARK_(coll) {
238
281
  return coll != null && isObj(coll);
239
282
  }
@@ -249,7 +292,7 @@ function typeConst(obj) {
249
292
  if (obj instanceof Map) return MAP_TYPE;
250
293
  if (obj instanceof Set) return SET_TYPE;
251
294
  if (obj instanceof List) return LIST_TYPE;
252
- if (Array.isArray(obj)) return ARRAY_TYPE;
295
+ if (isVectorArray(obj)) return ARRAY_TYPE;
253
296
  if (obj instanceof LazyIterable) return LAZY_ITERABLE_TYPE;
254
297
  if (obj instanceof SortedSet) return SET_TYPE;
255
298
 
@@ -399,14 +442,14 @@ export function conj(...xs) {
399
442
  case SET_TYPE:
400
443
  if (o instanceof SortedSet) {
401
444
  // prevent re-sorting of collection
402
- return conj_BANG_set(new o.constructor(o), rest);
445
+ return copyMeta(o, conj_BANG_set(new o.constructor(o), rest));
403
446
  } else {
404
- return new o.constructor([...o, ...rest]);
447
+ return copyMeta(o, new o.constructor([...o, ...rest]));
405
448
  }
406
449
  case LIST_TYPE:
407
- return new List(...rest.reverse(), ...o);
450
+ return copyMeta(o, new List(...rest.reverse(), ...o));
408
451
  case ARRAY_TYPE:
409
- return [...o, ...rest];
452
+ return copyMeta(o, [...o, ...rest]);
410
453
  case MAP_TYPE:
411
454
  m = new Map(o);
412
455
  for (const x of rest) {
@@ -417,7 +460,7 @@ export function conj(...xs) {
417
460
  else m.set(x[0], x[1]);
418
461
  }
419
462
 
420
- return m;
463
+ return copyMeta(o, m);
421
464
  case LAZY_ITERABLE_TYPE:
422
465
  return lazy(function* () {
423
466
  yield* rest;
@@ -431,7 +474,7 @@ export function conj(...xs) {
431
474
  else o2[x[0]] = x[1];
432
475
  }
433
476
 
434
- return o2;
477
+ return copyMeta(o, o2);
435
478
  default:
436
479
  throw new Error(
437
480
  'Illegal argument: conj expects a Set, Array, List, Map, or Object as the first argument.'
@@ -447,6 +490,7 @@ export function disj_BANG_(s, ...xs) {
447
490
  }
448
491
 
449
492
  export function disj(s, ...xs) {
493
+ if (s == null) return s;
450
494
  const s1 = new s.constructor([...s]);
451
495
  return disj_BANG_(s1, ...xs);
452
496
  }
@@ -503,26 +547,29 @@ export function println(...args) {
503
547
  }
504
548
 
505
549
  export function nth(coll, idx, orElse) {
506
- if (coll) {
507
- // "found" must be decided by the index bound, not by the value: an
508
- // in-bounds element that happens to be `undefined` is still found.
509
- if (Array.isArray(coll)) {
510
- if (idx >= 0 && idx < coll.length) {
511
- return coll[idx];
512
- }
513
- } else {
514
- // iterables may lack .length and can be infinite, so iterate and stop
515
- // at idx rather than computing a bound.
516
- const iter = iterable(coll);
517
- let i = 0;
518
- for (const value of iter) {
519
- if (i++ === idx) {
520
- return value;
521
- }
522
- }
550
+ const hasDefault = arguments.length > 2;
551
+ // nil coll puns to nil, like Clojure
552
+ if (coll == null) return hasDefault ? orElse : null;
553
+ // "found" is decided by the index bound, not the value. An in-bounds element
554
+ // that happens to be undefined is still found.
555
+ if (Array.isArray(coll)) {
556
+ if (idx >= 0 && idx < coll.length) {
557
+ return coll[idx];
558
+ }
559
+ } else if (idx >= 0) {
560
+ // non-array: skip whole chunks instead of counting elements (handles
561
+ // infinite seqs since it stops once idx is reached)
562
+ const next = chunkCursor(coll);
563
+ let base = 0;
564
+ let ch;
565
+ while ((ch = next()) !== null) {
566
+ if (idx < base + ch.length) return ch[idx - base];
567
+ base += ch.length;
523
568
  }
524
569
  }
525
- return orElse;
570
+ // out of bounds. With a default return it, otherwise throw like Clojure
571
+ if (hasDefault) return orElse;
572
+ throw new Error('Index out of bounds: ' + idx);
526
573
  }
527
574
 
528
575
  export function get(coll, key, otherwise = undefined) {
@@ -586,6 +633,21 @@ export function seqable_QMARK_(x) {
586
633
  );
587
634
  }
588
635
 
636
+ // squint has no distinct MapEntry type (map entries are plain 2-element
637
+ // arrays). We tag entries produced from a map with this marker symbol so
638
+ // map-entry? can tell them apart from ordinary vectors. Symbol-keyed props are
639
+ // invisible to =, into, iteration and JSON, so the effect is contained.
640
+ const MAP_ENTRY = Symbol('squint.lang.map-entry');
641
+
642
+ function tagMapEntry(e) {
643
+ e[MAP_ENTRY] = true;
644
+ return e;
645
+ }
646
+
647
+ export function map_entry_QMARK_(x) {
648
+ return Array.isArray(x) && x[MAP_ENTRY] === true;
649
+ }
650
+
589
651
  export function iterable(x) {
590
652
  // nil puns to empty iterable, support passing nil to first/rest/reduce, etc.
591
653
  if (x === null || x === undefined) {
@@ -597,7 +659,7 @@ export function iterable(x) {
597
659
  if (x[Symbol.iterator]) {
598
660
  return x;
599
661
  }
600
- if (x instanceof Object) return Object.entries(x);
662
+ if (x instanceof Object) return Object.entries(x).map(tagMapEntry);
601
663
  throw new TypeError(`${x} is not iterable`);
602
664
  }
603
665
 
@@ -624,12 +686,24 @@ export function seq(x) {
624
686
  }
625
687
 
626
688
  export function first(coll) {
689
+ if (coll == null) return undefined;
690
+ if (Array.isArray(coll)) return coll[0];
691
+ if (coll instanceof LazyIterable) {
692
+ coll.force();
693
+ return coll.chunk === null ? undefined : coll.chunk[0];
694
+ }
627
695
  // destructuring uses iterable protocol
628
696
  const [first] = iterable(coll);
629
697
  return first;
630
698
  }
631
699
 
632
700
  export function second(coll) {
701
+ if (coll instanceof LazyIterable) {
702
+ coll.force();
703
+ const ch = coll.chunk;
704
+ if (ch === null) return undefined;
705
+ return ch.length > 1 ? ch[1] : first(coll._rest);
706
+ }
633
707
  const [_, v] = iterable(coll);
634
708
  return v;
635
709
  }
@@ -639,13 +713,20 @@ export function ffirst(coll) {
639
713
  }
640
714
 
641
715
  export function rest(coll) {
642
- return lazy(function* () {
643
- let first = true;
644
- for (const x of iterable(coll)) {
645
- if (first) first = false;
646
- else yield x;
647
- }
648
- });
716
+ // chunk-aware: drop the first element of the first chunk, keep the rest of
717
+ // the chain chunked (preserves chunkedness, unlike re-iterating element-wise)
718
+ const cell = chunkCells(coll);
719
+ cell.force();
720
+ const ch = cell.chunk;
721
+ if (ch === null) return cell; // (rest ()) is ()
722
+ if (ch.length > 1) {
723
+ const c = new LazyIterable(null);
724
+ c.realized = true;
725
+ c.chunk = ch.slice(1);
726
+ c._rest = cell._rest;
727
+ return c;
728
+ }
729
+ return cell._rest; // first chunk had one element; the next cell is the rest
649
730
  }
650
731
 
651
732
  class Reduced {
@@ -660,16 +741,15 @@ class Reduced {
660
741
 
661
742
  export function last(coll) {
662
743
  coll = iterable(coll);
663
- let lastEl;
664
- switch (typeConst(coll)) {
665
- case ARRAY_TYPE:
666
- return coll[coll.length - 1];
667
- default:
668
- for (const x of coll) {
669
- lastEl = x;
670
- }
671
- return lastEl;
744
+ if (Array.isArray(coll)) {
745
+ return coll[coll.length - 1];
672
746
  }
747
+ // non-array: walk chunks, keep the last chunk's last element
748
+ const next = chunkCursor(coll);
749
+ let lastEl;
750
+ let ch;
751
+ while ((ch = next()) !== null) lastEl = ch[ch.length - 1];
752
+ return lastEl;
673
753
  }
674
754
 
675
755
  export function reduced(x) {
@@ -682,31 +762,43 @@ export function reduced_QMARK_(x) {
682
762
 
683
763
  export function reduce(f, arg1, arg2) {
684
764
  f = __toFn(f);
685
- let coll, val;
686
- if (arguments.length === 2) {
687
- // (reduce f coll)
688
- const iter = iterable(arg1)[Symbol.iterator]();
689
- const vd = iter.next();
690
- if (vd.done) {
691
- val = f();
692
- } else {
693
- val = vd.value;
765
+ const hasInit = arguments.length !== 2;
766
+ const coll = hasInit ? arg2 : arg1;
767
+ let val = hasInit ? arg1 : undefined;
768
+
769
+ // fast path: index loop over an array
770
+ if (Array.isArray(coll)) {
771
+ let i = 0;
772
+ if (!hasInit) {
773
+ if (coll.length === 0) return f();
774
+ val = coll[0];
775
+ i = 1;
694
776
  }
695
- coll = iter;
696
- } else {
697
- // (reduce f val coll)
698
- val = arg1;
699
- coll = iterable(arg2);
777
+ if (val instanceof Reduced) return val.value;
778
+ for (; i < coll.length; i++) {
779
+ val = f(val, coll[i]);
780
+ if (val instanceof Reduced) return val.value;
781
+ }
782
+ return val;
700
783
  }
701
- if (val instanceof Reduced) {
702
- return val.value;
784
+
785
+ // non-array: walk chunks (chunked cell or any other seqable)
786
+ const next = chunkCursor(coll);
787
+ let ch = next();
788
+ let i = 0;
789
+ if (!hasInit) {
790
+ if (ch === null) return f();
791
+ val = ch[0];
792
+ i = 1;
703
793
  }
704
- for (const x of coll) {
705
- val = f(val, x);
706
- if (val instanceof Reduced) {
707
- val = val.value;
708
- break;
794
+ if (val instanceof Reduced) return val.value;
795
+ while (ch !== null) {
796
+ for (; i < ch.length; i++) {
797
+ val = f(val, ch[i]);
798
+ if (val instanceof Reduced) return val.value;
709
799
  }
800
+ ch = next();
801
+ i = 0;
710
802
  }
711
803
  return val;
712
804
  }
@@ -739,42 +831,152 @@ function* _reductions3(f, init, coll) {
739
831
  export function reductions(f, arg1, arg2) {
740
832
  f = __toFn(f);
741
833
  if (arguments.length === 2) {
834
+ const it = es6_iterator(iterable(arg1));
742
835
  return lazy(function* () {
743
- yield* _reductions2(f, iterable(arg1)[Symbol.iterator]());
836
+ yield* _reductions2(f, it);
744
837
  });
745
838
  }
839
+ const it = es6_iterator(iterable(arg2));
746
840
  return lazy(function* () {
747
- yield* _reductions3(f, arg1, iterable(arg2)[Symbol.iterator]());
841
+ yield* _reductions3(f, arg1, it);
748
842
  });
749
843
  }
750
844
 
751
- var tolr = false;
845
+ // Deprecated: lazy values are now cached, so reuse no longer recomputes. Kept
846
+ // for API compatibility; remove in a future release.
752
847
  export function warn_on_lazy_reusage_BANG_() {
753
- tolr = true;
848
+ console.warn(
849
+ 'warn-on-lazy-reusage! is deprecated and does nothing: lazy values are now cached.',
850
+ );
754
851
  }
755
852
 
853
+ const CHUNK_SIZE = 32;
854
+
855
+ // One cell of a self-caching chunked seq. `step` is a thunk returning
856
+ // [nonEmptyChunkArray, nextStep] or null at the end. See doc/dev/lazy-seqs.md.
756
857
  class LazyIterable {
757
- constructor(gen) {
758
- this.gen = gen;
759
- this.usages = 0;
760
- }
761
- [Symbol.iterator]() {
762
- this.usages++;
763
- if (this.usages >= 2 && tolr) {
764
- try {
765
- throw new Error();
766
- } catch (e) {
767
- console.warn('Re-use of lazy value', e.stack);
858
+ constructor(step) {
859
+ this.step = step;
860
+ this.realized = false;
861
+ this.chunk = null; // array, or null when this is the terminal (empty) cell
862
+ this._rest = null;
863
+ }
864
+ force() {
865
+ if (!this.realized) {
866
+ this.realized = true;
867
+ const r = this.step();
868
+ this.step = null;
869
+ if (r !== null && r !== undefined) {
870
+ this.chunk = r[0];
871
+ this._rest = new LazyIterable(r[1]);
768
872
  }
769
873
  }
770
- return this.gen();
874
+ return this;
875
+ }
876
+ [Symbol.iterator]() {
877
+ let cell = this;
878
+ let i = 0;
879
+ return {
880
+ next() {
881
+ for (;;) {
882
+ cell.force();
883
+ const ch = cell.chunk;
884
+ if (ch === null) return { value: undefined, done: true };
885
+ if (i < ch.length) return { value: ch[i++], done: false };
886
+ cell = cell._rest;
887
+ i = 0;
888
+ }
889
+ },
890
+ [Symbol.iterator]() {
891
+ return this;
892
+ },
893
+ };
771
894
  }
772
895
  }
773
896
 
774
897
  LazyIterable.prototype[IIterable] = true; // Closure compatibility
775
898
 
899
+ // One-element chunks: an unchunked seq, realized one element at a time.
900
+ function unchunkedSteps(iter) {
901
+ const step = () => {
902
+ const r = iter.next();
903
+ return r.done ? null : [[r.value], step];
904
+ };
905
+ return step;
906
+ }
907
+
908
+ // f is a zero-arg generator function; its result is an unchunked lazy seq.
776
909
  export function lazy(f) {
777
- return new LazyIterable(f);
910
+ return new LazyIterable(unchunkedSteps(f()));
911
+ }
912
+
913
+ // gen(it) over a hoisted iterator: keeps the input head unpinned (streaming)
914
+ function lazyIter(coll, gen) {
915
+ const it = es6_iterator(iterable(coll));
916
+ return lazy(() => gen(it));
917
+ }
918
+
919
+ // A chunked view of any seqable for chunk-aware ops, preserving chunkedness:
920
+ // cells pass through, arrays slice into CHUNK_SIZE batches, others stay unchunked.
921
+ function chunkCells(coll) {
922
+ if (coll instanceof LazyIterable) return coll;
923
+ if (Array.isArray(coll)) {
924
+ const step = (pos) => () => {
925
+ if (pos >= coll.length) return null;
926
+ const end = Math.min(pos + CHUNK_SIZE, coll.length);
927
+ return [coll.slice(pos, end), step(end)];
928
+ };
929
+ return new LazyIterable(step(0));
930
+ }
931
+ return new LazyIterable(unchunkedSteps(es6_iterator(iterable(coll))));
932
+ }
933
+
934
+ // A cursor over a seq's chunks for realizers: returns a function yielding the
935
+ // next chunk array or null. Drive it with an inline loop (keeps an accumulator a
936
+ // plain local, unlike a callback). Callers keep their own array shortcut.
937
+ function chunkCursor(coll) {
938
+ if (coll instanceof LazyIterable) {
939
+ let cell = coll;
940
+ return () => {
941
+ if (cell === null) return null;
942
+ cell.force();
943
+ const ch = cell.chunk;
944
+ cell = ch === null ? null : cell._rest;
945
+ return ch;
946
+ };
947
+ }
948
+ const it = es6_iterator(iterable(coll));
949
+ return () => {
950
+ const b = [];
951
+ for (let i = 0; i < CHUNK_SIZE; i++) {
952
+ const r = it.next();
953
+ if (r.done) break;
954
+ b.push(r.value);
955
+ }
956
+ return b.length === 0 ? null : b;
957
+ };
958
+ }
959
+
960
+ // Build a lazy seq transforming each input chunk via xf(chunk, baseIndex) -> new
961
+ // chunk array, preserving chunkedness. Empty results are skipped (e.g. filter).
962
+ // baseIndex is the input element count before the chunk, for indexed ops.
963
+ function mapChunks(coll, xf) {
964
+ const src = chunkCells(coll);
965
+ const step = (cell, base) => () => {
966
+ let c = cell;
967
+ let b = base;
968
+ for (;;) {
969
+ c.force();
970
+ const ch = c.chunk;
971
+ if (ch === null) return null;
972
+ const out = xf(ch, b);
973
+ const rest = c._rest;
974
+ b += ch.length;
975
+ if (out.length !== 0) return [out, step(rest, b)];
976
+ c = rest;
977
+ }
978
+ };
979
+ return new LazyIterable(step(src, 0));
778
980
  }
779
981
 
780
982
  export class Cons {
@@ -782,24 +984,35 @@ export class Cons {
782
984
  this.x = x;
783
985
  this.coll = coll;
784
986
  }
785
- *[Symbol.iterator]() {
786
- yield this.x;
787
- yield* iterable(this.coll);
987
+ [Symbol.iterator]() {
988
+ const x = this.x;
989
+ let coll = this.coll;
990
+ let started = false;
991
+ let it = null;
992
+ return {
993
+ next() {
994
+ if (!started) {
995
+ started = true;
996
+ return { value: x, done: false };
997
+ }
998
+ if (!it) {
999
+ it = es6_iterator(iterable(coll));
1000
+ coll = null; // release the tail head so a single pass streams
1001
+ }
1002
+ return it.next();
1003
+ },
1004
+ [Symbol.iterator]() {
1005
+ return this;
1006
+ },
1007
+ };
788
1008
  }
789
1009
  }
790
1010
 
791
1011
  export function cons(x, coll) {
792
1012
  return new Cons(x, coll);
793
- // return lazy(function* () {
794
- // yield x;
795
- // yield* iterable(coll);
796
- // });
797
1013
  }
798
1014
 
799
1015
  export function map(f, ...colls) {
800
- // if (! (f instanceof Function)) {
801
- // throw new Error(`Argument f must be a function but is ${typeof(f)}`);
802
- // }
803
1016
  f = __toFn(f);
804
1017
  switch (colls.length) {
805
1018
  case 0:
@@ -822,14 +1035,14 @@ export function map(f, ...colls) {
822
1035
  };
823
1036
  };
824
1037
  case 1:
825
- return lazy(function* () {
826
- for (const x of iterable(colls[0])) {
827
- yield f(x);
828
- }
1038
+ return mapChunks(colls[0], (ch) => {
1039
+ const out = new Array(ch.length);
1040
+ for (let i = 0; i < ch.length; i++) out[i] = f(ch[i]);
1041
+ return out;
829
1042
  });
830
- default:
1043
+ default: {
1044
+ const iters = colls.map((coll) => es6_iterator(iterable(coll)));
831
1045
  return lazy(function* () {
832
- const iters = colls.map((coll) => es6_iterator(iterable(coll)));
833
1046
  while (true) {
834
1047
  const args = [];
835
1048
  for (const i of iters) {
@@ -842,45 +1055,41 @@ export function map(f, ...colls) {
842
1055
  yield f(...args);
843
1056
  }
844
1057
  });
1058
+ }
845
1059
  }
846
1060
  }
847
1061
 
848
- function filter1(pred) {
1062
+ // 0/1 arities pass through to rf; step(rf) is the 2-arity reducer
1063
+ function transducer(step) {
849
1064
  return (rf) => {
850
- return (...args) => {
851
- switch (args.length) {
852
- case 0:
853
- return rf();
854
- case 1:
855
- return rf(args[0]);
856
- case 2: {
857
- const result = args[0];
858
- const input = args[1];
859
- if (truth_(pred(input))) {
860
- return rf(result, input);
861
- } else return result;
862
- }
863
- }
864
- };
1065
+ const s = step(rf);
1066
+ return (...args) =>
1067
+ args.length === 0 ? rf() : args.length === 1 ? rf(args[0]) : s(args[0], args[1]);
865
1068
  };
866
1069
  }
867
1070
 
1071
+ function filter1(pred) {
1072
+ return transducer((rf) => (r, x) => (truth_(pred(x)) ? rf(r, x) : r));
1073
+ }
1074
+
868
1075
  export function filter(pred, coll) {
869
1076
  if (arguments.length === 1) {
870
1077
  return filter1(pred);
871
1078
  }
872
1079
  pred = __toFn(pred);
873
- return lazy(function* () {
874
- for (const x of iterable(coll)) {
875
- if (truth_(pred(x))) {
876
- yield x;
877
- }
1080
+ return mapChunks(coll, (ch) => {
1081
+ const out = [];
1082
+ for (let i = 0; i < ch.length; i++) {
1083
+ const x = ch[i];
1084
+ if (truth_(pred(x))) out.push(x);
878
1085
  }
1086
+ return out;
879
1087
  });
880
1088
  }
881
1089
 
882
1090
  export function filterv(pred, coll) {
883
- return [...filter(pred, coll)];
1091
+ // filter is chunked; vec bulk-appends its chunks
1092
+ return pushAll([], filter(pred, coll));
884
1093
  }
885
1094
 
886
1095
  export function remove(pred, coll) {
@@ -891,19 +1100,10 @@ export function remove(pred, coll) {
891
1100
  }
892
1101
 
893
1102
  function map_indexed1(f) {
894
- return (rf) => {
1103
+ return transducer((rf) => {
895
1104
  let i = -1;
896
- return (...args) => {
897
- switch (args.length) {
898
- case 0:
899
- return rf();
900
- case 1:
901
- return rf(args[0]);
902
- case 2:
903
- return rf(args[0], f(((i = i + 1), i), args[1]));
904
- }
905
- };
906
- };
1105
+ return (r, x) => rf(r, f(++i, x));
1106
+ });
907
1107
  }
908
1108
 
909
1109
  export function map_indexed(f, coll) {
@@ -911,50 +1111,33 @@ export function map_indexed(f, coll) {
911
1111
  if (arguments.length === 1) {
912
1112
  return map_indexed1(f);
913
1113
  }
914
- return lazy(function* () {
915
- let idx = 0;
916
- for (const i of iterable(coll)) {
917
- yield f(idx, i);
918
- idx++;
919
- }
1114
+ return mapChunks(coll, (ch, base) => {
1115
+ const out = new Array(ch.length);
1116
+ for (let i = 0; i < ch.length; i++) out[i] = f(base + i, ch[i]);
1117
+ return out;
920
1118
  });
921
1119
  }
922
1120
 
923
1121
  function keep_indexed2(f, coll) {
924
1122
  f = __toFn(f);
925
- return lazy(function* () {
926
- let idx = 0;
927
- for (const i of iterable(coll)) {
928
- const v = f(idx, i);
929
- if (truth_(v)) yield v;
930
- idx++;
1123
+ return mapChunks(coll, (ch, base) => {
1124
+ const out = [];
1125
+ for (let i = 0; i < ch.length; i++) {
1126
+ const v = f(base + i, ch[i]);
1127
+ if (truth_(v)) out.push(v);
931
1128
  }
1129
+ return out;
932
1130
  });
933
1131
  }
934
1132
 
935
1133
  function keep_indexed1(f) {
936
- return (rf) => {
1134
+ return transducer((rf) => {
937
1135
  let ia = -1;
938
- return (...args) => {
939
- const al = args.length;
940
- if (al === 0) {
941
- return rf();
942
- }
943
- if (al === 1) {
944
- return rf(args[0]);
945
- }
946
- if (al === 2) {
947
- const result = args[0];
948
- const input = args[1];
949
- ia++;
950
- const v = f(ia, input);
951
- if (v == null) {
952
- return result;
953
- }
954
- return rf(result, v);
955
- }
1136
+ return (r, x) => {
1137
+ const v = f(++ia, x);
1138
+ return v == null ? r : rf(r, v);
956
1139
  };
957
- };
1140
+ });
958
1141
  }
959
1142
 
960
1143
  export function keep_indexed(f, coll) {
@@ -970,7 +1153,12 @@ export function str(...xs) {
970
1153
  }
971
1154
 
972
1155
  export function name(x) {
973
- if (typeof x === 'string') return x;
1156
+ if (typeof x === 'string') {
1157
+ // keywords/symbols are strings in squint; name is the part after the "/"
1158
+ // ns separator (consistent with `namespace`, which returns the part before)
1159
+ const i = x.indexOf('/');
1160
+ return i >= 1 ? x.slice(i + 1) : x;
1161
+ }
974
1162
  throw new Error("Doesn't support name: " + typeof x);
975
1163
  }
976
1164
 
@@ -1055,22 +1243,29 @@ export function compare_and_set_BANG_(atm, oldv, newv) {
1055
1243
  }
1056
1244
 
1057
1245
  export function range(begin, end, step) {
1058
- return lazy(function* () {
1059
- let b = begin,
1060
- e = end,
1061
- s = step;
1062
- if (end === undefined) {
1063
- b = 0;
1064
- e = begin;
1065
- }
1066
- let i = b || 0;
1067
- s = step ?? 1;
1068
- const ascending = s >= 0;
1069
- while (e === undefined || (ascending && i < e) || (!ascending && e < i)) {
1070
- yield i;
1246
+ // range is a chunked source: it realizes CHUNK_SIZE values at a time
1247
+ let b = begin,
1248
+ e = end,
1249
+ s = step;
1250
+ if (end === undefined) {
1251
+ b = 0;
1252
+ e = begin;
1253
+ }
1254
+ const start = b || 0;
1255
+ s = step ?? 1;
1256
+ const ascending = s >= 0;
1257
+ const more = (i) => e === undefined || (ascending && i < e) || (!ascending && e < i);
1258
+ const mkStep = (from) => () => {
1259
+ if (!more(from)) return null;
1260
+ const out = [];
1261
+ let i = from;
1262
+ while (out.length < CHUNK_SIZE && more(i)) {
1263
+ out.push(i);
1071
1264
  i += s;
1072
1265
  }
1073
- });
1266
+ return [out, mkStep(i)];
1267
+ };
1268
+ return new LazyIterable(mkStep(start));
1074
1269
  }
1075
1270
 
1076
1271
  export function re_matches(re, s) {
@@ -1127,7 +1322,7 @@ export function vector(...args) {
1127
1322
  export const array = vector;
1128
1323
 
1129
1324
  export function vector_QMARK_(x) {
1130
- return typeConst(x) === ARRAY_TYPE;
1325
+ return isVectorArray(x);
1131
1326
  }
1132
1327
 
1133
1328
  export function mapv(...args) {
@@ -1143,9 +1338,12 @@ export function mapv(...args) {
1143
1338
  }
1144
1339
  return ret;
1145
1340
  } else {
1146
- var ret = [];
1147
- for (const x of iter) {
1148
- ret.push(f(x));
1341
+ // non-array: map whole chunks straight into the result array
1342
+ const ret = [];
1343
+ const next = chunkCursor(iter);
1344
+ let ch;
1345
+ while ((ch = next()) !== null) {
1346
+ for (let i = 0; i < ch.length; i++) ret.push(f(ch[i]));
1149
1347
  }
1150
1348
  return ret;
1151
1349
  }
@@ -1153,12 +1351,32 @@ export function mapv(...args) {
1153
1351
  return [...map(...args)];
1154
1352
  }
1155
1353
 
1156
- export function vec(x) {
1157
- if (array_QMARK_(x)) {
1158
- // return original, no need to clone the entire thing
1159
- return x;
1354
+ // Append every element of `from` to array `out`, bulk-appending whole chunks
1355
+ // for a chunked seq. Returns out.
1356
+ function pushAll(out, from) {
1357
+ if (from instanceof LazyIterable) {
1358
+ let cell = from;
1359
+ for (;;) {
1360
+ cell.force();
1361
+ const ch = cell.chunk;
1362
+ if (ch === null) return out;
1363
+ Array.prototype.push.apply(out, ch);
1364
+ cell = cell._rest;
1365
+ }
1160
1366
  }
1161
- return [...iterable(x)];
1367
+ for (const x of iterable(from)) out.push(x);
1368
+ return out;
1369
+ }
1370
+
1371
+ // Materialize a seq into a fresh, mutable array (bulk-copies chunked seqs).
1372
+ function toArray(coll) {
1373
+ if (coll instanceof LazyIterable) return pushAll([], coll);
1374
+ return [...iterable(coll)];
1375
+ }
1376
+
1377
+ export function vec(x) {
1378
+ if (isVectorArray(x)) return x;
1379
+ return pushAll([], x);
1162
1380
  }
1163
1381
 
1164
1382
  export function set(coll) {
@@ -1223,12 +1441,54 @@ export function array_QMARK_(x) {
1223
1441
  return Array.isArray(x);
1224
1442
  }
1225
1443
 
1444
+ const CONCAT_DONE = Symbol('concat-done');
1445
+
1226
1446
  function concat1(colls) {
1227
- return lazy(function* () {
1228
- for (const coll of colls) {
1229
- yield* iterable(coll);
1447
+ // chunk-aware: pass each coll's chunks through, preserving chunkedness. Each
1448
+ // coll head is released once consumed so a single pass streams.
1449
+ const isArr = Array.isArray(colls);
1450
+ const arr = isArr ? colls.slice() : null;
1451
+ const collIter = isArr ? null : es6_iterator(iterable(colls));
1452
+ let idx = 0;
1453
+ const nextColl = () => {
1454
+ if (isArr) {
1455
+ if (idx >= arr.length) return CONCAT_DONE;
1456
+ const c = arr[idx];
1457
+ arr[idx] = null;
1458
+ idx++;
1459
+ return c;
1230
1460
  }
1231
- });
1461
+ const r = collIter.next();
1462
+ return r.done ? CONCAT_DONE : r.value;
1463
+ };
1464
+ // src is null, a chunked cell, or {a, pos} for an array (raw-array fast path:
1465
+ // no per-coll wrapper). Continuations carry immutable state, so a single pass
1466
+ // streams.
1467
+ const step = (src) => () => {
1468
+ for (;;) {
1469
+ if (src !== null) {
1470
+ if (src instanceof LazyIterable) {
1471
+ src.force();
1472
+ if (src.chunk !== null) return [src.chunk, step(src._rest)];
1473
+ src = null;
1474
+ } else {
1475
+ const a = src.a;
1476
+ const pos = src.pos;
1477
+ if (pos < a.length) {
1478
+ const end = Math.min(pos + CHUNK_SIZE, a.length);
1479
+ // slice (copy) so a cached chunk never aliases the caller's array
1480
+ return [a.slice(pos, end), step({ a, pos: end })];
1481
+ }
1482
+ src = null;
1483
+ }
1484
+ }
1485
+ const nc = nextColl();
1486
+ if (nc === CONCAT_DONE) return null;
1487
+ src =
1488
+ nc instanceof LazyIterable ? nc : Array.isArray(nc) ? { a: nc, pos: 0 } : chunkCells(nc);
1489
+ }
1490
+ };
1491
+ return new LazyIterable(step(null));
1232
1492
  }
1233
1493
 
1234
1494
  export function concat(...colls) {
@@ -1243,10 +1503,8 @@ concat[IApply__apply] = (colls) => {
1243
1503
  export function mapcat(f, ...colls) {
1244
1504
  if (colls.length === 0) {
1245
1505
  return comp(map(f), cat);
1246
- } else {
1247
- const mapped = map(f, ...colls);
1248
- return concat1(mapped);
1249
1506
  }
1507
+ return concat1(map(f, ...colls));
1250
1508
  }
1251
1509
 
1252
1510
  export function identity(x) {
@@ -1254,8 +1512,8 @@ export function identity(x) {
1254
1512
  }
1255
1513
 
1256
1514
  export function interleave(...colls) {
1515
+ const iters = colls.map((coll) => es6_iterator(iterable(coll)));
1257
1516
  return lazy(function* () {
1258
- const iters = colls.map((coll) => es6_iterator(iterable(coll)));
1259
1517
  while (true) {
1260
1518
  const res = [];
1261
1519
  for (const i of iters) {
@@ -1271,30 +1529,17 @@ export function interleave(...colls) {
1271
1529
  }
1272
1530
 
1273
1531
  function interpose1(sep) {
1274
- return (rf) => {
1532
+ return transducer((rf) => {
1275
1533
  let started = false;
1276
- return (...args) => {
1277
- switch (args.length) {
1278
- case 0:
1279
- return rf();
1280
- case 1:
1281
- return rf(args[0]);
1282
- case 2: {
1283
- if (started) {
1284
- const sepr = rf(args[0], sep);
1285
- if (reduced_QMARK_(sepr)) {
1286
- return sepr;
1287
- } else {
1288
- return rf(sepr, args[1]);
1289
- }
1290
- } else {
1291
- started = true;
1292
- return rf(args[0], args[1]);
1293
- }
1294
- }
1534
+ return (r, x) => {
1535
+ if (!started) {
1536
+ started = true;
1537
+ return rf(r, x);
1295
1538
  }
1539
+ const sepr = rf(r, sep);
1540
+ return reduced_QMARK_(sepr) ? sepr : rf(sepr, x);
1296
1541
  };
1297
- };
1542
+ });
1298
1543
  }
1299
1544
 
1300
1545
  export function interpose(sep, coll) {
@@ -1386,10 +1631,10 @@ export const partitionv = partition; // partition already returns a lazy of arra
1386
1631
  export const partitionv_all = partition_all;
1387
1632
 
1388
1633
  function partitionInternal(n, step, pad, coll, all) {
1389
- return lazy(function* () {
1634
+ return lazyIter(coll, function* (it) {
1390
1635
  let p = [];
1391
1636
  let i = 0;
1392
- for (const x of iterable(coll)) {
1637
+ for (const x of it) {
1393
1638
  if (i < n) {
1394
1639
  p.push(x);
1395
1640
  if (p.length === n) {
@@ -1461,8 +1706,8 @@ export function partition_by(f, coll) {
1461
1706
  if (arguments.length === 1) {
1462
1707
  return partition_by1(f);
1463
1708
  }
1709
+ const iter = es6_iterator(coll);
1464
1710
  return lazy(function* () {
1465
- const iter = es6_iterator(coll);
1466
1711
  const _fst = iter.next();
1467
1712
  if (_fst.done) {
1468
1713
  yield* null;
@@ -1495,7 +1740,7 @@ export function partition_by(f, coll) {
1495
1740
  export function empty(coll) {
1496
1741
  const type = typeConst(coll);
1497
1742
  if (type != null) {
1498
- return emptyOfType(type);
1743
+ return copyMeta(coll, emptyOfType(type));
1499
1744
  } else {
1500
1745
  throw new Error(`Can't create empty of ${typeof coll}`);
1501
1746
  }
@@ -1561,7 +1806,13 @@ export function into(...args) {
1561
1806
  case 1:
1562
1807
  return args[0];
1563
1808
  case 2:
1564
- return conj(args[0] ?? [], ...iterable(args[1]));
1809
+ // vector target bulk-appends chunks (copy preserves metadata); lists conj
1810
+ // at the head, other targets need conj!
1811
+ to = args[0] ?? [];
1812
+ if (isVectorArray(to)) {
1813
+ return pushAll(copy(to), args[1]);
1814
+ }
1815
+ return reduce(conj_BANG_, copy(to), args[1]);
1565
1816
  case 3:
1566
1817
  to = args[0];
1567
1818
  xform = args[1];
@@ -1612,42 +1863,23 @@ export function ensure_reduced(x) {
1612
1863
  }
1613
1864
 
1614
1865
  function take1(n) {
1615
- return (rf) => {
1866
+ return transducer((rf) => {
1616
1867
  let na = n;
1617
- return (...args) => {
1618
- const al = args.length;
1619
- if (al === 0) {
1620
- return rf();
1621
- }
1622
- if (al === 1) {
1623
- const result = args[0];
1624
- return rf(result);
1625
- }
1626
- if (al === 2) {
1627
- let result = args[0];
1628
- const input = args[1];
1629
- const n = na;
1630
- const nn = ((na = na - 1), na);
1631
- if (n > 0) {
1632
- result = rf(result, input);
1633
- }
1634
- if (!(nn > 0)) {
1635
- return ensure_reduced(result);
1636
- } else {
1637
- return result;
1638
- }
1639
- }
1868
+ return (r, x) => {
1869
+ const nn = --na + 1;
1870
+ if (nn > 0) r = rf(r, x);
1871
+ return nn > 1 ? r : ensure_reduced(r);
1640
1872
  };
1641
- };
1873
+ });
1642
1874
  }
1643
1875
 
1644
1876
  export function take(n, coll) {
1645
1877
  if (arguments.length === 1) {
1646
1878
  return take1(n);
1647
1879
  }
1648
- return lazy(function* () {
1880
+ return lazyIter(coll, function* (it) {
1649
1881
  let i = n - 1;
1650
- for (const x of iterable(coll)) {
1882
+ for (const x of it) {
1651
1883
  if (i-- >= 0) {
1652
1884
  yield x;
1653
1885
  }
@@ -1681,22 +1913,7 @@ export function take_last(n, coll) {
1681
1913
  }
1682
1914
 
1683
1915
  function take_while1(pred) {
1684
- return (rf) => {
1685
- return (...args) => {
1686
- const al = args.length;
1687
- if (al === 0) return rf();
1688
- if (al === 1) return rf(args[0]);
1689
- if (al === 2) {
1690
- const result = args[0];
1691
- const input = args[1];
1692
- if (truth_(pred(input))) {
1693
- return rf(result, input);
1694
- } else {
1695
- return reduced(result);
1696
- }
1697
- }
1698
- };
1699
- };
1916
+ return transducer((rf) => (r, x) => (truth_(pred(x)) ? rf(r, x) : reduced(r)));
1700
1917
  }
1701
1918
 
1702
1919
  export function take_while(pred, coll) {
@@ -1704,8 +1921,8 @@ export function take_while(pred, coll) {
1704
1921
  if (arguments.length === 1) {
1705
1922
  return take_while1(pred);
1706
1923
  }
1707
- return lazy(function* () {
1708
- for (const o of iterable(coll)) {
1924
+ return lazyIter(coll, function* (it) {
1925
+ for (const o of it) {
1709
1926
  if (truth_(pred(o))) yield o;
1710
1927
  else return;
1711
1928
  }
@@ -1713,23 +1930,10 @@ export function take_while(pred, coll) {
1713
1930
  }
1714
1931
 
1715
1932
  function take_nth1(n) {
1716
- return (rf) => {
1933
+ return transducer((rf) => {
1717
1934
  let ia = -1;
1718
- return (...args) => {
1719
- const al = args.length;
1720
- if (al === 0) return rf();
1721
- if (al === 1) return rf(args[0]);
1722
- if (al === 2) {
1723
- const result = args[0];
1724
- const input = args[1];
1725
- ia++;
1726
- const i = ia;
1727
- if (rem(i, n) === 0) {
1728
- return rf(result, input);
1729
- } else return result;
1730
- }
1731
- };
1732
- };
1935
+ return (r, x) => (rem(++ia, n) === 0 ? rf(r, x) : r);
1936
+ });
1733
1937
  }
1734
1938
 
1735
1939
  export function take_nth(n, coll) {
@@ -1738,9 +1942,9 @@ export function take_nth(n, coll) {
1738
1942
  return repeat(first(coll));
1739
1943
  }
1740
1944
 
1741
- return lazy(function* () {
1945
+ return lazyIter(coll, function* (it) {
1742
1946
  let i = 0;
1743
- for (const x of iterable(coll)) {
1947
+ for (const x of it) {
1744
1948
  if (i % n === 0) {
1745
1949
  yield x;
1746
1950
  }
@@ -1763,33 +1967,15 @@ export function cycle(coll) {
1763
1967
  }
1764
1968
 
1765
1969
  function drop1(n) {
1766
- return (rf) => {
1970
+ return transducer((rf) => {
1767
1971
  let na = n;
1768
- return (...args) => {
1769
- const al = args.length;
1770
- if (al === 0) {
1771
- return rf();
1772
- }
1773
- if (al === 1) {
1774
- return rf(args[0]);
1775
- }
1776
- if (al === 2) {
1777
- const result = args[0];
1778
- const input = args[1];
1779
- const n = na;
1780
- na--;
1781
- if (n > 0) {
1782
- return result;
1783
- } else return rf(result, input);
1784
- }
1785
- };
1786
- };
1972
+ return (r, x) => (na-- > 0 ? r : rf(r, x));
1973
+ });
1787
1974
  }
1788
1975
 
1789
1976
  export function drop(n, xs) {
1790
1977
  if (arguments.length === 1) return drop1(n);
1791
- return lazy(function* () {
1792
- const iter = _iterator(iterable(xs));
1978
+ return lazyIter(xs, function* (iter) {
1793
1979
  for (let x = 0; x < n; x++) {
1794
1980
  iter.next();
1795
1981
  }
@@ -1798,36 +1984,20 @@ export function drop(n, xs) {
1798
1984
  }
1799
1985
 
1800
1986
  function drop_while1(pred) {
1801
- return (rf) => {
1987
+ return transducer((rf) => {
1802
1988
  let da = true;
1803
- return (...args) => {
1804
- const al = args.length;
1805
- if (al === 0) {
1806
- return rf();
1807
- }
1808
- if (al === 1) {
1809
- return rf(args[0]);
1810
- }
1811
- if (al === 2) {
1812
- const isDrop = da;
1813
- const result = args[0];
1814
- const input = args[1];
1815
- if (isDrop && truth_(pred(input))) {
1816
- return result;
1817
- } else {
1818
- da = null;
1819
- return rf(result, input);
1820
- }
1821
- }
1989
+ return (r, x) => {
1990
+ if (da && truth_(pred(x))) return r;
1991
+ da = false;
1992
+ return rf(r, x);
1822
1993
  };
1823
- };
1994
+ });
1824
1995
  }
1825
1996
 
1826
1997
  export function drop_while(pred, xs) {
1827
1998
  pred = __toFn(pred);
1828
1999
  if (arguments.length === 1) return drop_while1(pred);
1829
- return lazy(function* () {
1830
- const iter = _iterator(iterable(xs));
2000
+ return lazyIter(xs, function* (iter) {
1831
2001
  while (true) {
1832
2002
  const nextItem = iter.next();
1833
2003
  if (nextItem.done) {
@@ -1844,28 +2014,21 @@ export function drop_while(pred, xs) {
1844
2014
  }
1845
2015
 
1846
2016
  function distinct1() {
1847
- return (rf) => {
2017
+ return transducer((rf) => {
1848
2018
  const seen = new Set();
1849
- return (...args) => {
1850
- const al = args.length;
1851
- if (al === 0) return rf();
1852
- if (al === 1) return rf(args[0]);
1853
- if (al === 2) {
1854
- const result = args[0];
1855
- const input = args[1];
1856
- if (seen.has(input)) return result;
1857
- seen.add(input);
1858
- return rf(result, input);
1859
- }
2019
+ return (r, x) => {
2020
+ if (seen.has(x)) return r;
2021
+ seen.add(x);
2022
+ return rf(r, x);
1860
2023
  };
1861
- };
2024
+ });
1862
2025
  }
1863
2026
 
1864
2027
  export function distinct(coll) {
1865
2028
  if (arguments.length === 0) return distinct1();
1866
- return lazy(function* () {
2029
+ return lazyIter(coll, function* (it) {
1867
2030
  const seen = new Set();
1868
- for (const x of iterable(coll)) {
2031
+ for (const x of it) {
1869
2032
  if (!seen.has(x)) yield x;
1870
2033
  seen.add(x);
1871
2034
  }
@@ -1876,28 +2039,21 @@ export function distinct(coll) {
1876
2039
  const DEDUPE_NONE = Symbol('dedupe-none');
1877
2040
 
1878
2041
  function dedupe1() {
1879
- return (rf) => {
2042
+ return transducer((rf) => {
1880
2043
  let prev = DEDUPE_NONE;
1881
- return (...args) => {
1882
- const al = args.length;
1883
- if (al === 0) return rf();
1884
- if (al === 1) return rf(args[0]);
1885
- if (al === 2) {
1886
- const result = args[0];
1887
- const input = args[1];
1888
- const skip = prev !== DEDUPE_NONE && truth_(_EQ_(prev, input));
1889
- prev = input;
1890
- return skip ? result : rf(result, input);
1891
- }
2044
+ return (r, x) => {
2045
+ const skip = prev !== DEDUPE_NONE && truth_(_EQ_(prev, x));
2046
+ prev = x;
2047
+ return skip ? r : rf(r, x);
1892
2048
  };
1893
- };
2049
+ });
1894
2050
  }
1895
2051
 
1896
2052
  export function dedupe(coll) {
1897
2053
  if (arguments.length === 0) return dedupe1();
1898
- return lazy(function* () {
2054
+ return lazyIter(coll, function* (it) {
1899
2055
  let prev = DEDUPE_NONE;
1900
- for (const x of iterable(coll)) {
2056
+ for (const x of it) {
1901
2057
  if (prev === DEDUPE_NONE || !truth_(_EQ_(prev, x))) yield x;
1902
2058
  prev = x;
1903
2059
  }
@@ -1937,8 +2093,14 @@ export function fnil(f, x, ...xs) {
1937
2093
 
1938
2094
  export function every_QMARK_(pred, coll) {
1939
2095
  pred = __toFn(pred);
1940
- for (const x of iterable(coll)) {
1941
- if (!pred(x)) return false;
2096
+ if (Array.isArray(coll)) {
2097
+ for (let i = 0; i < coll.length; i++) if (!pred(coll[i])) return false;
2098
+ return true;
2099
+ }
2100
+ const next = chunkCursor(coll);
2101
+ let ch;
2102
+ while ((ch = next()) !== null) {
2103
+ for (let i = 0; i < ch.length; i++) if (!pred(ch[i])) return false;
1942
2104
  }
1943
2105
  return true;
1944
2106
  }
@@ -1948,36 +2110,27 @@ export function not_every_QMARK_(pred, coll) {
1948
2110
  }
1949
2111
 
1950
2112
  function keep1(pred) {
1951
- return (rf) => {
1952
- return (...args) => {
1953
- const al = args.length;
1954
- if (al === 0) return rf();
1955
- if (al === 1) return rf(args[0]);
1956
- if (al === 2) {
1957
- const result = args[0];
1958
- const input = args[1];
1959
- const v = pred(input);
1960
- if (v == null) return result;
1961
- return rf(result, v);
1962
- }
1963
- };
1964
- };
2113
+ return transducer((rf) => (r, x) => {
2114
+ const v = pred(x);
2115
+ return v == null ? r : rf(r, v);
2116
+ });
1965
2117
  }
1966
2118
 
1967
2119
  export function keep(pred, coll) {
1968
2120
  pred = __toFn(pred);
1969
2121
  if (arguments.length === 1) return keep1(pred);
1970
- return lazy(function* () {
1971
- for (const o of iterable(coll)) {
1972
- const res = pred(o);
1973
- if (truth_(res)) yield res;
2122
+ return mapChunks(coll, (ch) => {
2123
+ const out = [];
2124
+ for (let i = 0; i < ch.length; i++) {
2125
+ const res = pred(ch[i]);
2126
+ if (truth_(res)) out.push(res);
1974
2127
  }
2128
+ return out;
1975
2129
  });
1976
2130
  }
1977
2131
 
1978
2132
  export function reverse(coll) {
1979
- coll = iterable(coll);
1980
- return [...coll].reverse();
2133
+ return toArray(coll).reverse();
1981
2134
  }
1982
2135
 
1983
2136
  export function sort(f, coll) {
@@ -1986,9 +2139,8 @@ export function sort(f, coll) {
1986
2139
  f = undefined;
1987
2140
  }
1988
2141
  f = __toFn(f);
1989
- coll = iterable(coll);
1990
- // we need to clone coll since .sort works in place and .toSorted isn't available on Node < 20
1991
- const clone = [...coll];
2142
+ // need a copy since .sort works in place and .toSorted isn't on Node < 20
2143
+ const clone = toArray(coll);
1992
2144
  // result is guaranteed to be stable since ES2019, like CLJS
1993
2145
  return clone.sort(f || compare);
1994
2146
  }
@@ -2028,7 +2180,7 @@ export function sort_by(keyfn, comp, coll) {
2028
2180
  }
2029
2181
 
2030
2182
  export function shuffle(coll) {
2031
- const result = [...coll];
2183
+ const result = toArray(coll);
2032
2184
  let remaining = result.length;
2033
2185
  while (remaining) {
2034
2186
  const i = Math.floor(Math.random() * remaining--);
@@ -2042,9 +2194,20 @@ export function shuffle(coll) {
2042
2194
 
2043
2195
  export function some(pred, coll) {
2044
2196
  pred = __toFn(pred);
2045
- for (const o of iterable(coll)) {
2046
- const res = pred(o);
2047
- if (truth_(res)) return res;
2197
+ if (Array.isArray(coll)) {
2198
+ for (let i = 0; i < coll.length; i++) {
2199
+ const res = pred(coll[i]);
2200
+ if (truth_(res)) return res;
2201
+ }
2202
+ return undefined;
2203
+ }
2204
+ const next = chunkCursor(coll);
2205
+ let ch;
2206
+ while ((ch = next()) !== null) {
2207
+ for (let i = 0; i < ch.length; i++) {
2208
+ const res = pred(ch[i]);
2209
+ if (truth_(res)) return res;
2210
+ }
2048
2211
  }
2049
2212
  return undefined;
2050
2213
  }
@@ -2130,22 +2293,16 @@ export function frequencies(coll) {
2130
2293
  return res;
2131
2294
  }
2132
2295
 
2133
- export class LazySeq {
2296
+ // The lazy-seq macro emits `new LazySeq(() => body)`: a LazyIterable whose step
2297
+ // evaluates the body thunk on first force and reads it unchunked.
2298
+ export class LazySeq extends LazyIterable {
2134
2299
  constructor(f) {
2135
- this.f = f;
2136
- this.res = undefined;
2137
- }
2138
- *[Symbol.iterator]() {
2139
- if (this.res === undefined) {
2140
- this.res = this.f();
2141
- this.f = null;
2142
- }
2143
- yield* iterable(this.res);
2300
+ super(() => unchunkedSteps(es6_iterator(iterable(f())))());
2144
2301
  }
2145
2302
  }
2146
2303
 
2147
2304
  export function butlast(coll) {
2148
- const x = [...iterable(coll)];
2305
+ const x = toArray(coll);
2149
2306
  x.pop();
2150
2307
  return x.length > 0 ? x : null;
2151
2308
  }
@@ -2169,10 +2326,11 @@ export function count(coll) {
2169
2326
  if (typeof len === 'number') {
2170
2327
  return len;
2171
2328
  }
2329
+ // sum chunk lengths instead of counting elements one by one
2330
+ const next = chunkCursor(coll);
2172
2331
  let ret = 0;
2173
- for (const _ of iterable(coll)) {
2174
- ret++;
2175
- }
2332
+ let ch;
2333
+ while ((ch = next()) !== null) ret += ch.length;
2176
2334
  return ret;
2177
2335
  }
2178
2336
 
@@ -2240,6 +2398,12 @@ export function aset(arr, idx, val, ...more) {
2240
2398
  }
2241
2399
 
2242
2400
  export function dorun(x) {
2401
+ // only a lazy seq needs forcing; realized colls are walked natively
2402
+ if (x instanceof LazyIterable) {
2403
+ const next = chunkCursor(x);
2404
+ while (next() !== null);
2405
+ return null;
2406
+ }
2243
2407
  for (const _ of iterable(x)) {
2244
2408
  // nothing here, just consume for side effects
2245
2409
  }
@@ -2501,6 +2665,62 @@ export function string_QMARK_(s) {
2501
2665
  return typeof s === 'string';
2502
2666
  }
2503
2667
 
2668
+ export function unchecked_int(x) {
2669
+ return Math.trunc(x);
2670
+ }
2671
+
2672
+ export function unchecked_inc_int(x) {
2673
+ return x + 1;
2674
+ }
2675
+
2676
+ export function unchecked_dec_int(x) {
2677
+ return x - 1;
2678
+ }
2679
+
2680
+ export function unchecked_add_int(x, y) {
2681
+ return x + y;
2682
+ }
2683
+
2684
+ // keywords/symbols are strings in squint; namespace is the part before "/"
2685
+ export function namespace(x) {
2686
+ // keywords/symbols are strings in squint; namespace is the part before the
2687
+ // "/" ns separator (consistent with `name`, which returns the part after).
2688
+ // i >= 1 so a leading "/" yields a nil namespace rather than an empty one.
2689
+ const i = x.indexOf('/');
2690
+ return i >= 1 ? x.slice(0, i) : null;
2691
+ }
2692
+
2693
+ // squint has no keyword type; keywords are strings, so keyword/keyword? are
2694
+ // string-based. (keyword name) or (keyword ns name).
2695
+ export function keyword(arg1, arg2) {
2696
+ if (arg2 !== undefined) {
2697
+ return (arg1 != null ? arg1 + '/' : '') + arg2;
2698
+ }
2699
+ return arg1;
2700
+ }
2701
+
2702
+ export function keyword_QMARK_(x) {
2703
+ return typeof x === 'string';
2704
+ }
2705
+
2706
+ // squint has no symbol type either; symbols are strings
2707
+ export function symbol_QMARK_(x) {
2708
+ return typeof x === 'string';
2709
+ }
2710
+
2711
+ // squint has no first-class Var objects (#'x emits the value), so var? is false
2712
+ export function var_QMARK_(_x) {
2713
+ return false;
2714
+ }
2715
+
2716
+ export function simple_keyword_QMARK_(x) {
2717
+ return typeof x === 'string' && !x.includes('/');
2718
+ }
2719
+
2720
+ export function qualified_keyword_QMARK_(x) {
2721
+ return typeof x === 'string' && x.includes('/');
2722
+ }
2723
+
2504
2724
  export function coll_QMARK_(coll) {
2505
2725
  return typeConst(coll) != undefined;
2506
2726
  }
@@ -2554,8 +2774,6 @@ export function neg_int_QMARK_(x) {
2554
2774
  return int_QMARK_(x) && x < 0;
2555
2775
  }
2556
2776
 
2557
- const _metaSym = Symbol('meta');
2558
-
2559
2777
  export function meta(x) {
2560
2778
  if (x instanceof Object) {
2561
2779
  return x[_metaSym];
@@ -2604,7 +2822,7 @@ export function bounded_count(n, coll) {
2604
2822
  export function find(m, k) {
2605
2823
  const v = get(m, k);
2606
2824
  if (v !== undefined) {
2607
- return [k, v];
2825
+ return tagMapEntry([k, v]);
2608
2826
  }
2609
2827
  }
2610
2828
 
@@ -2773,7 +2991,9 @@ export function transient$(x) {
2773
2991
  }
2774
2992
 
2775
2993
  export function persistent_BANG_(x) {
2776
- return Object.freeze(x);
2994
+ // no Object.freeze: persistent structures stay extensible so symbol-keyed
2995
+ // metadata can be attached
2996
+ return x;
2777
2997
  }
2778
2998
 
2779
2999
  class SortedSet {
@@ -3060,6 +3280,7 @@ export function volatile_BANG_(x) {
3060
3280
 
3061
3281
  export function vreset_BANG_(vol, v) {
3062
3282
  vol.v = v;
3283
+ return v;
3063
3284
  }
3064
3285
 
3065
3286
  function toEDN(value, seen = new WeakSet()) {