Source src/silo:std.collections
1##! Collection trait hierarchy and generic collection helpers. 2##! 3##! Declares the abstract-collection traits (`Foldable`, `Mappable`, 4##! `Filterable`, `Keyed`, `Chainable`, `Indexed`, `Zippable`) and the 5##! category aliases (`Seq`, `Map`, `Set`) that classify concrete types 6##! like `Vec`, `HashMap`, and `HashSet`. 7 8:use 9 :open core Bool AnyInt Vec Pair Option None Some RangeBoth Ordering Less Equal Greater Result Ok Err Unit 10 :open collections.hashmap HashMap 11 :open collections.hashset HashSet 12 :open traits Eq Add Not Ord Default 13 :open effects.random random-gen-u64 14:end 15 16# si[impl coll.hashmap.hash-state+2] 17## Opaque, seeded hasher state used internally by `HashMap` and `HashSet` 18## for HashDoS resistance. The canonical constructor `(HashState .new)` 19## draws entropy via `+Random`. The free fn `hash-state-new` is retained 20## as a zero-seed helper for tests that need a deterministic `HashState` 21## without carrying an effect. 22## 23## Deferred (lands with the HashMap/HashSet `.default` rewire): 24## - Threading `+Random` through `(HashMap .default)` / `(HashSet 25## .default)`. That is a prelude-semver change (both types drop out 26## of the prelude) and cascades through every construction site, so 27## it is gated behind effect-handler infra. 28:record(pub) HashState 29 .seed AnyInt 30:end 31 32# si[impl coll.hashmap.hash-state+2] 33## Construct a `HashState` seeded from a fresh `+Random` draw. This is 34## the spec-canonical form of `(HashState .new)`; callers that need a 35## deterministic zero-seeded state (e.g. bootstrap tests) use 36## `hash-state-new` instead. 37:impl HashState 38 .new ( -> HashState ) +Random 39 random-gen-u64 HashState ; 40:end 41 42# si[impl coll.hashmap.hash-state+2] 43## Construct a `HashState` with a fixed zero seed. Deterministic sibling 44## of `(HashState .new)` used by stdlib code that needs a `HashState` 45## without carrying `+Random`. 46## ( -> HashState ) 47:fn(pub) hash-state-new ( -> HashState ) 48 0 HashState 49:end 50 51# si[impl coll.hashmap.hash-state+2] 52# si[impl lang.default+1] 53## `HashMap`'s `.default` MUST draw a seeded `HashState` via 54## `(HashState .new)`, carrying `+Random`. The HashState is produced 55## and dropped here because `hashmap-new-intrinsic` does not yet 56## accept a state argument (the runtime HashMap does not thread the 57## seed through its bucket hasher). The declared `+Random` effect is 58## still load-bearing: it documents that `HashMap` construction is 59## non-deterministic per the HashDoS-resistance contract and forces 60## callers to either sit inside a `+Random` handler or use an 61## explicit zero-seed path through `hash-state-new` + 62## `hashmap-new-intrinsic`. 63:impl Default (HashMap key val) 64 .default ( -> (HashMap key val) ) +Random 65 (HashState .new) drop hashmap-new-intrinsic ; 66:end 67 68# si[impl coll.hashmap.hash-state+2] 69# si[impl lang.default+1] 70## `HashSet`'s `.default` mirrors `HashMap`'s: draws `(HashState .new)` 71## and discards the state, carrying `+Random`. 72:impl Default (HashSet elem) 73 .default ( -> (HashSet elem) ) +Random 74 (HashState .new) drop hashset-new-intrinsic ; 75:end 76 77## Classic while-loop word: repeatedly evaluate `cond`; while it yields 78## `true`, invoke `body`. Stops the first time `cond` produces `false`. 79## ( [..s -> ..s Bool] [..s -> ..s] -> ) 80:fn(pub) while ( [..s -> ..s Bool] [..s -> ..s] -> ) 81 pop-> body pop-> cond 82 :loop cond .call not :if :break :end body .call :end 83:end 84 85# si[impl coll.foldable] 86# si[impl coll.foldable.default] 87## Trait for collections whose elements can be consumed by folding. 88## The most general collection surface: any type that can produce its 89## elements one at a time implements `Foldable`. `.fold` and `.for-each` 90## are required; `.any`, `.all`, `.count` are defaults derived from 91## `.fold`. 92@lang(foldable) 93:trait(pub) Foldable 94 ## Reduce the collection to a single value by threading `acc` through 95 ## a binary quotation applied to each element in iteration order. 96 ## ( (Self elem) acc [acc elem -> acc] -> acc ) 97 .fold ( (Self elem) acc [acc elem -> acc] -> acc ) 98 ## Apply a quotation to each element for its effects; no result value. 99 ## ( (Self elem) [elem ->] -> ) 100 .for-each ( (Self elem) [elem ->] -> ) 101 ## Return `true` if `pred` holds for any element. 102 ## ( (Self elem) [elem -> Bool] -> Bool ) 103 .any ( (Self elem) [elem -> Bool] -> Bool ) 104 pop-> pred false [ pop-> e pop-> acc e pred .call acc or ] .fold ; 105 ## Return `true` if `pred` holds for every element. 106 ## ( (Self elem) [elem -> Bool] -> Bool ) 107 .all ( (Self elem) [elem -> Bool] -> Bool ) 108 pop-> pred true [ pop-> e pop-> acc e pred .call acc and ] .fold ; 109 ## Return the number of elements. 110 ## ( (Self elem) -> AnyInt ) 111 .count ( (Self elem) -> AnyInt ) 112 0 [ drop 1 + ] .fold ; 113 ## Return `Some elem` for the first element, else `None` on empty. 114 ## Not spec'd but a natural Foldable default. 115 ## ( (Self elem) -> (Option elem) ) 116 .first ( (Self elem) -> (Option elem) ) 117 None [ pop-> e pop-> acc 118 acc :match 119 | Some x => x Some 120 | None => e Some 121 :end 122 ] .fold ; 123 ## Return `Some elem` for the last element, else `None` on empty. 124 ## Not spec'd but a natural Foldable default. 125 ## ( (Self elem) -> (Option elem) ) 126 .last ( (Self elem) -> (Option elem) ) 127 None [ pop-> e drop e Some ] .fold ; 128 # si[impl stdlib.iter.find+1] 129 ## Return `Some elem` for the first element satisfying `pred`, else 130 ## `None`. No short-circuit — visits every element; keeps the first 131 ## match. 132 ## ( (Self elem) [elem -> Bool] -> (Option elem) ) 133 .find ( (Self elem) [elem -> Bool] -> (Option elem) ) 134 pop-> pred 135 None [ pop-> e pop-> acc 136 acc :match 137 | Some x => x Some 138 | None => e pred .call :if e Some :else None :end 139 :end 140 ] .fold ; 141 # si[impl stdlib.iter.reduce] 142 ## Reduce the collection with a binary combiner. Returns `None` on empty. 143 ## ( (Self elem) [elem elem -> elem] -> (Option elem) ) 144 .reduce ( (Self elem) [elem elem -> elem] -> (Option elem) ) 145 pop-> f 146 None [ pop-> e pop-> acc 147 acc :match 148 | Some x => x e f .call Some 149 | None => e Some 150 :end 151 ] .fold ; 152 # si[impl stdlib.iter.find-map] 153 ## Return `Some b` for the first quotation call that yields `Some b`. 154 ## Produces `None` if every element maps to `None`. 155 ## ( (Self elem) [elem -> (Option b)] -> (Option b) ) 156 .find-map ( (Self elem) [elem -> (Option b)] -> (Option b) ) 157 pop-> f 158 None [ pop-> e pop-> acc 159 acc :match 160 | Some x => x Some 161 | None => e f .call 162 :end 163 ] .fold ; 164 # si[impl stdlib.iter.try-fold] 165 ## Short-circuiting fold. Threads the accumulator through a combiner 166 ## returning `(Result acc err)`; stops at the first `Err` and returns 167 ## it unchanged. On empty, returns `init Ok`. 168 ## ( (Self elem) acc [acc elem -> (Result acc err)] -> (Result acc err) ) 169 .try-fold ( (Self elem) acc [acc elem -> (Result acc err)] -> (Result acc err) ) 170 pop-> f pop-> init 171 init Ok [ pop-> e pop-> r 172 r :match 173 | Ok a => a e f .call 174 | Err er => er Err 175 :end 176 ] .fold ; 177 # si[impl stdlib.iter.try-for-each] 178 ## Short-circuiting for-each. Calls an effectful quotation returning 179 ## `(Result Unit err)` per element; stops at the first `Err`. On an 180 ## empty collection or all-`Ok` walk, returns `unit Ok`. 181 ## ( (Self elem) [elem -> (Result Unit err)] -> (Result Unit err) ) 182 .try-for-each ( (Self elem) [elem -> (Result Unit err)] -> (Result Unit err) ) 183 pop-> f 184 unit Ok [ pop-> e pop-> r 185 r :match 186 | Ok _ => e f .call 187 | Err er => er Err 188 :end 189 ] .fold ; 190 # si[impl stdlib.iter.try-reduce] 191 ## Short-circuiting reduce. Empty collection yields `None Ok`. The 192 ## first element seeds the reduction; subsequent elements are 193 ## combined with the current value via `f`, whose `(Result elem err)` 194 ## result short-circuits on `Err`. 195 ## ( (Self elem) [elem elem -> (Result elem err)] -> (Result (Option elem) err) ) 196 .try-reduce ( (Self elem) [elem elem -> (Result elem err)] -> (Result (Option elem) err) ) 197 pop-> f 198 None Ok [ pop-> e pop-> r 199 r :match 200 | Err er => er Err 201 | Ok maybe => maybe :match 202 | None => e Some Ok 203 | Some x => x e f .call :match 204 | Ok y => y Some Ok 205 | Err er2 => er2 Err 206 :end 207 :end 208 :end 209 ] .fold ; 210 # si[impl stdlib.iter.min-by+1] 211 ## Return `Some elem` for the minimum element per comparator, else `None`. 212 ## ( (Self elem) [elem elem -> Ordering] -> (Option elem) ) 213 .min-by ( (Self elem) [elem elem -> Ordering] -> (Option elem) ) 214 pop-> cmp 215 [ pop-> rhs pop-> lhs 216 lhs rhs cmp .call :match 217 | Greater => rhs 218 | Less => lhs 219 | Equal => lhs 220 :end 221 ] .reduce ; 222 # si[impl stdlib.iter.max-by+1] 223 ## Return `Some elem` for the maximum element per comparator, else `None`. 224 ## ( (Self elem) [elem elem -> Ordering] -> (Option elem) ) 225 .max-by ( (Self elem) [elem elem -> Ordering] -> (Option elem) ) 226 pop-> cmp 227 [ pop-> rhs pop-> lhs 228 lhs rhs cmp .call :match 229 | Greater => lhs 230 | Less => rhs 231 | Equal => lhs 232 :end 233 ] .reduce ; 234 # si[impl stdlib.iter.enumerate+1] 235 ## Pair each element with its zero-based index. Fold with a 236 ## `(Pair AnyInt (Vec (Pair AnyInt elem)))` accumulator: `.a` 237 ## tracks the next index, `.b` collects `Pair idx elem` entries. 238 ## Returns the accumulator Pair as-is (index on `.a`, entries on 239 ## `.b`); projecting `.b` inline in the body trips the checker 240 ## on a spurious `elem = Vec (Pair AnyInt elem)` self-reference, 241 ## and projecting at the call site also loses type info. Callers 242 ## that need just the Vec bind with `pop->` and project then. 243 ## ( (Self elem) -> (Pair AnyInt (Vec (Pair AnyInt elem))) ) 244 .enumerate ( (Self elem) -> (Pair AnyInt (Vec (Pair AnyInt elem))) ) 245 0 (Vec .default) Pair [ pop-> e pop-> acc 246 acc .a pop-> i 247 acc .b pop-> vec 248 i 1 + vec i e Pair .push Pair 249 ] .fold ; 250 # si[impl stdlib.iter.position+1] 251 ## Return `Some AnyInt` for the zero-based index of the first element 252 ## satisfying `pred`, else `None`. Fold with a 253 ## `(Pair AnyInt (Option AnyInt))` accumulator. 254 ## ( (Self elem) [elem -> Bool] -> (Option AnyInt) ) 255 .position ( (Self elem) [elem -> Bool] -> (Option AnyInt) ) 256 pop-> pred 257 0 None Pair [ pop-> e pop-> acc 258 acc .a pop-> i 259 acc .b pop-> hit 260 hit :match 261 | Some x => i 1 + x Some Pair 262 | None => e pred .call 263 :if i 1 + i Some Pair 264 :else i 1 + None Pair 265 :end 266 :end 267 ] .fold 268 .b ; 269 # si[impl stdlib.iter.take+1] 270 ## Return the first `n` elements as a `Vec`. Fold with a 271 ## `(Pair AnyInt (Vec elem))` accumulator; once the collected count 272 ## reaches `n`, subsequent elements are ignored. 273 ## ( (Self elem) AnyInt -> (Vec elem) ) 274 .take ( (Self elem) AnyInt -> (Vec elem) ) 275 pop-> n 276 0 (Vec .default) Pair [ pop-> e pop-> acc 277 acc .a pop-> i 278 acc .b pop-> vec 279 i n < :if 280 i 1 + vec e .push Pair 281 :else 282 i 1 + vec Pair 283 :end 284 ] .fold 285 .b ; 286 # si[impl stdlib.iter.skip+1] 287 ## Return all elements after the first `n`. Fold with a 288 ## `(Pair AnyInt (Vec elem))` accumulator; elements are pushed only 289 ## once the seen-count has reached `n`. 290 ## ( (Self elem) AnyInt -> (Vec elem) ) 291 .skip ( (Self elem) AnyInt -> (Vec elem) ) 292 pop-> n 293 0 (Vec .default) Pair [ pop-> e pop-> acc 294 acc .a pop-> i 295 acc .b pop-> vec 296 i n < :if 297 i 1 + vec Pair 298 :else 299 i 1 + vec e .push Pair 300 :end 301 ] .fold 302 .b ; 303 # si[impl stdlib.iter.partition+1] 304 ## Split elements by a predicate into two `Vec`s: the deeper Vec 305 ## contains elements where `pred` returned `true`, the top Vec 306 ## contains the rest. Matches spec: two Seqs on stack. 307 ## ( (Self elem) [elem -> Bool] -> (Vec elem la) (Vec elem lb) ) 308 .partition ( (Self elem) [elem -> Bool] -> (Vec elem la) (Vec elem lb) ) 309 pop-> pred 310 (Vec .default) (Vec .default) Pair [ pop-> e pop-> acc 311 acc .a pop-> trues 312 acc .b pop-> falses 313 e pred .call :if 314 trues e .push falses Pair 315 :else 316 trues falses e .push Pair 317 :end 318 ] .fold 319 pop-> result 320 result .a result .b ; 321 # si[impl stdlib.iter.take-while+1] 322 ## Take elements from the start as long as `pred` returns `true`. 323 ## The first element for which `pred` returns `false` stops collection 324 ## — no later elements are included, even if they would match. Fold 325 ## with a `(Pair Bool (Vec elem))` accumulator: `.a` is the still- 326 ## taking flag, `.b` is the collected Vec. Once the flag flips false 327 ## it stays false. 328 ## ( (Self elem) [elem -> Bool] -> (Vec elem) ) 329 .take-while ( (Self elem) [elem -> Bool] -> (Vec elem) ) 330 pop-> pred 331 true (Vec .default) Pair [ pop-> e pop-> acc 332 acc .a pop-> taking 333 acc .b pop-> vec 334 taking :if 335 e pred .call :if 336 true vec e .push Pair 337 :else 338 false vec Pair 339 :end 340 :else 341 false vec Pair 342 :end 343 ] .fold 344 .b ; 345 # si[impl stdlib.iter.skip-while+1] 346 ## Skip elements from the start as long as `pred` returns `true`. 347 ## Once `pred` returns `false` for some element, that element and 348 ## every element after it are collected regardless of the predicate. 349 ## Fold with a `(Pair Bool (Vec elem))` accumulator: `.a` is the 350 ## still-skipping flag, `.b` is the collected Vec. 351 ## ( (Self elem) [elem -> Bool] -> (Vec elem) ) 352 .skip-while ( (Self elem) [elem -> Bool] -> (Vec elem) ) 353 pop-> pred 354 true (Vec .default) Pair [ pop-> e pop-> acc 355 acc .a pop-> skipping 356 acc .b pop-> vec 357 skipping :if 358 e pred .call :if 359 true vec Pair 360 :else 361 false vec e .push Pair 362 :end 363 :else 364 false vec e .push Pair 365 :end 366 ] .fold 367 .b ; 368 # si[impl stdlib.iter.min-by-key] 369 ## Return `Some elem` for the element with the smallest key 370 ## extracted by `f`, else `None` on an empty collection. Fold with 371 ## an `(Option (Pair elem key))` accumulator so the `.a` / `.b` 372 ## field projections pin the tuple shape through the fold. Uses 373 ## `.<` on keys — the `(Ord key key)` constraint must supply it. 374 ## ( (Self elem) [elem -> key] -> (Option elem) ) { (Ord key key) } 375 .min-by-key ( (Self elem) [elem -> key] -> (Option elem) ) { (Ord key key) } 376 pop-> f 377 None [ pop-> e pop-> acc 378 e f .call pop-> k 379 acc :match 380 | None => e k Pair Some 381 | Some bp => 382 bp .a pop-> be 383 bp .b pop-> bk 384 k bk .< :if e k Pair Some :else be bk Pair Some :end 385 :end 386 ] .fold 387 :match 388 | None => None 389 | Some bp => bp .a Some 390 :end ; 391 # si[impl stdlib.iter.max-by-key] 392 ## Return `Some elem` for the element with the largest key 393 ## extracted by `f`, else `None` on an empty collection. Mirrors 394 ## `.min-by-key` but replaces `.<` with `.>`. 395 ## ( (Self elem) [elem -> key] -> (Option elem) ) { (Ord key key) } 396 .max-by-key ( (Self elem) [elem -> key] -> (Option elem) ) { (Ord key key) } 397 pop-> f 398 None [ pop-> e pop-> acc 399 e f .call pop-> k 400 acc :match 401 | None => e k Pair Some 402 | Some bp => 403 bp .a pop-> be 404 bp .b pop-> bk 405 k bk .> :if e k Pair Some :else be bk Pair Some :end 406 :end 407 ] .fold 408 :match 409 | None => None 410 | Some bp => bp .a Some 411 :end ; 412 ## Count the elements for which `pred` returns `true`. Pure fold 413 ## with an integer counter. Not specced as its own iter rule — this 414 ## is a natural Foldable default that sits between `.count` 415 ## (unconditional) and `.any`/`.all` (boolean aggregation). 416 ## ( (Self elem) [elem -> Bool] -> AnyInt ) 417 .count-where ( (Self elem) [elem -> Bool] -> AnyInt ) 418 pop-> pred 419 0 [ pop-> e pop-> acc 420 e pred .call :if acc 1 + :else acc :end 421 ] .fold ; 422 # si[impl stdlib.iter.filter-map] 423 ## Fused filter+map: call `f` on each element and collect the 424 ## `Some` payloads into a `Vec`, skipping `None`s. Wraps the 425 ## output Vec in a single-field Pair-like accumulator (the 426 ## unit pairing) so the checker pins the element type through 427 ## fold — bare `(Vec .default)` as the fold acc leaves the 428 ## element polymorphic and `.push` fails to resolve at install. 429 ## ( (Self elem) [elem -> (Option b)] -> (Vec b) ) 430 .filter-map ( (Self elem) [elem -> (Option b)] -> (Vec b) ) 431 pop-> f 432 unit (Vec .default) Pair [ pop-> e pop-> acc 433 acc .a pop-> u 434 acc .b pop-> vec 435 e f .call :match 436 | Some x => u vec x .push Pair 437 | None => u vec Pair 438 :end 439 ] .fold 440 .b ; 441 # si[impl stdlib.iter.map-while] 442 ## Like `.filter-map`, but the first `None` result stops iteration; 443 ## subsequent elements are not consulted. Fold with a 444 ## `(Pair Bool (Vec b))` accumulator — `.a` is the still-taking flag, 445 ## `.b` is the collected Vec. 446 ## ( (Self elem) [elem -> (Option b)] -> (Vec b) ) 447 .map-while ( (Self elem) [elem -> (Option b)] -> (Vec b) ) 448 pop-> f 449 true (Vec .default) Pair [ pop-> e pop-> acc 450 acc .a pop-> taking 451 acc .b pop-> vec 452 taking :if 453 e f .call :match 454 | Some x => true vec x .push Pair 455 | None => false vec Pair 456 :end 457 :else 458 false vec Pair 459 :end 460 ] .fold 461 .b ; 462 # si[impl stdlib.iter.is-partitioned] 463 ## Return `true` if every element for which `pred` returns `true` 464 ## appears before every element for which it returns `false`. An 465 ## empty collection is considered partitioned. Fold with a 466 ## `(Pair Bool Bool)` accumulator: `.a` is the still-ok flag, 467 ## `.b` records whether any `false`-predicate element has been 468 ## observed yet. Seeing a `true` after a `false` breaks the 469 ## invariant. 470 ## ( (Self elem) [elem -> Bool] -> Bool ) 471 .is-partitioned ( (Self elem) [elem -> Bool] -> Bool ) 472 pop-> pred 473 true false Pair [ pop-> e pop-> acc 474 acc .a pop-> ok 475 acc .b pop-> seen-false 476 ok :if 477 e pred .call :if 478 seen-false :if 479 false true Pair 480 :else 481 true false Pair 482 :end 483 :else 484 true true Pair 485 :end 486 :else 487 false true Pair 488 :end 489 ] .fold 490 .a ; 491 # si[impl stdlib.iter.scan] 492 ## Stateful transform. Thread an initial state through a stepper 493 ## `[state elem -> state (Option result)]`. Each `Some r` 494 ## contributes `r` to the output `Vec`; the first `None` stops 495 ## iteration. Fold with a `(Pair (Option state) (Vec result))` 496 ## accumulator — outer `.a` is `Some state` while running and 497 ## `None` once terminated, outer `.b` is the collected output. 498 ## The `:match` on `.a` pins the Option shape through fold so 499 ## the body's field dispatch and `.push` on the Vec resolve. 500 ## ( (Self elem) state [state elem -> state (Option result)] -> (Vec result) ) 501 .scan ( (Self elem) state [state elem -> state (Option result)] -> (Vec result) ) 502 pop-> f pop-> init 503 init Some (Vec .default) Pair [ pop-> e pop-> acc 504 acc .a pop-> mstate 505 acc .b pop-> vec 506 mstate :match 507 | None => None vec Pair 508 | Some s => 509 s e f .call pop-> opt pop-> s2 510 opt :match 511 | Some r => s2 Some vec r .push Pair 512 | None => None vec Pair 513 :end 514 :end 515 ] .fold 516 .b ; 517 # si[impl stdlib.iter.step-by] 518 ## Return every `stride`-th element starting with the first (index 0). 519 ## Fold with a `(Pair AnyInt (Vec elem))` accumulator: `.a` is the 520 ## running element index, `.b` is the collected output. An element is 521 ## included when `i mod stride = 0`. A stride of 1 returns every 522 ## element; the spec flags stride=0 as a compile-time error — this 523 ## default doesn't enforce that at the trait level. 524 ## ( (Self elem) AnyInt -> (Vec elem) ) 525 .step-by ( (Self elem) AnyInt -> (Vec elem) ) 526 pop-> stride 527 0 (Vec .default) Pair [ pop-> e pop-> acc 528 acc .a pop-> i 529 acc .b pop-> vec 530 i stride mod 0 = :if 531 i 1 + vec e .push Pair 532 :else 533 i 1 + vec Pair 534 :end 535 ] .fold 536 .b ; 537 # si[impl stdlib.iter.rposition] 538 ## Return `Some AnyInt` for the zero-based index of the last element 539 ## satisfying `pred`, else `None`. Fold visits every element, 540 ## overwriting the tracked match-index each time the predicate holds. 541 ## Accumulator is `(Pair AnyInt (Option AnyInt))`: `.a` is the running 542 ## index, `.b` is the most recent matching index (or `None`). 543 ## ( (Self elem) [elem -> Bool] -> (Option AnyInt) ) 544 .rposition ( (Self elem) [elem -> Bool] -> (Option AnyInt) ) 545 pop-> pred 546 0 None Pair [ pop-> e pop-> acc 547 acc .a pop-> i 548 acc .b pop-> last 549 e pred .call :if 550 i 1 + i Some Pair 551 :else 552 i 1 + last Pair 553 :end 554 ] .fold 555 .b ; 556 # si[impl stdlib.iter.chunks-n] 557 ## Partition a Foldable into fixed-size chunks. The last chunk MAY 558 ## have fewer than `n` elements if the input length is not a 559 ## multiple of `n`. Default is a stub; real body lives in the 560 ## `Foldable (Vec elem | len)` impl override where the element 561 ## type and outer accumulator are concretely bound. 562 ## ( (Self elem) AnyInt -> (Vec (Vec elem)) ) 563 .chunks-n ( (Self elem) AnyInt -> (Vec (Vec elem)) ) 564 drop drop (Vec .default) ; 565 # si[impl stdlib.iter.intersperse] 566 ## Insert `sep` between every adjacent pair of input elements. An 567 ## input with fewer than two elements MUST NOT include the separator. 568 ## Fold with a `(Pair Bool (Vec elem))` accumulator: `.a` tracks 569 ## whether the next element is the first (no leading separator); 570 ## `.b` is the collected output. The first element is pushed alone, 571 ## subsequent elements are pushed after a separator. 572 ## ( (Self elem) elem -> (Vec elem) ) 573 .intersperse ( (Self elem) elem -> (Vec elem) ) 574 pop-> sep 575 true (Vec .default) Pair [ pop-> e pop-> acc 576 acc .a pop-> first 577 acc .b pop-> vec 578 first :if 579 false vec e .push Pair 580 :else 581 false vec sep .push e .push Pair 582 :end 583 ] .fold 584 .b ; 585 # si[impl stdlib.iter.unzip] 586 ## Split a Foldable of `Pair a b` into two Vecs of the `.a` / `.b` 587 ## components. Default is a stub; real body lives in the 588 ## `Foldable (Vec elem | len)` impl override where the element is 589 ## concretely Pair-shaped. 590 ## ( (Self (Pair a b)) -> (Pair (Vec a) (Vec b)) ) 591 .unzip ( (Self (Pair a b)) -> (Pair (Vec a) (Vec b)) ) 592 drop (Vec .default) (Vec .default) Pair ; 593 # si[impl stdlib.iter.flatten+1] 594 ## Concatenate a Foldable-of-Vecs into a single flat `Vec`. Default 595 ## is a stub; real body lives in the Vec impl override (requires 596 ## the element to be Vec-shaped). 597 ## ( (Self (Vec elem n)) -> (Vec elem) ) 598 .flatten ( (Self (Vec elem n)) -> (Vec elem) ) 599 drop (Vec .default) ; 600 # si[impl stdlib.iter.flat-map+1] 601 ## Map each element to a `Vec` via `f`, then concatenate. Default 602 ## is a stub; real body lives in the Vec impl override. 603 ## ( (Self elem) [elem -> (Vec b n)] -> (Vec b) ) 604 .flat-map ( (Self elem) [elem -> (Vec b n)] -> (Vec b) ) 605 drop drop (Vec .default) ; 606:end 607 608:impl Foldable (Vec elem len) 609 .fold ( (Vec elem len) acc [acc elem -> acc] -> acc ) 610 pop-> f pop-> acc pop-> vec 0 pop-> i 611 :loop i vec .len = :if acc :ret :end 612 acc i vec .get f .call pop-> acc 613 i 1 + pop-> i 614 :end ; 615 .for-each ( (Vec elem len) [elem ->] -> ) 616 pop-> f pop-> vec 0 pop-> i 617 :loop i vec .len = :if :ret :end 618 i vec .get f .call 619 i 1 + pop-> i 620 :end ; 621 # si[impl stdlib.iter.flatten+1] 622 ## Vec-specific flatten: nested fold pushing inner Vec's elements 623 ## into the outer accumulator. Element type is concretely 624 ## Vec-shaped here, so `.push` on the accumulator resolves. 625 .flatten ( (Vec (Vec inner-elem)) -> (Vec inner-elem) ) 626 (Vec .default) [ pop-> inner pop-> acc 627 acc inner [ pop-> e pop-> acc2 acc2 e .push ] .fold 628 ] .fold ; 629 # si[impl stdlib.iter.flat-map+1] 630 ## Vec-specific flat-map: call `f` per element, flatten the results. 631 .flat-map ( (Vec elem) [elem -> (Vec b)] -> (Vec b) ) 632 pop-> f 633 (Vec .default) [ pop-> e pop-> acc 634 acc e f .call [ pop-> x pop-> acc2 acc2 x .push ] .fold 635 ] .fold ; 636 # si[impl stdlib.iter.chunks-n] 637 ## Vec-specific chunks-n: Pair(completed_chunks, current_chunk) 638 ## accumulator; finaliser flushes any non-empty trailing chunk. 639 .chunks-n ( (Vec elem) AnyInt -> (Vec (Vec elem)) ) 640 pop-> n 641 (Vec .default) (Vec .default) Pair [ pop-> e pop-> acc 642 acc .a pop-> chunks 643 acc .b pop-> current 644 current e .push pop-> current 645 current .len n = :if 646 chunks current .push (Vec .default) Pair 647 :else 648 chunks current Pair 649 :end 650 ] .fold 651 pop-> final 652 final .a pop-> chunks 653 final .b pop-> current 654 current .len 0 > :if 655 chunks current .push 656 :else 657 chunks 658 :end ; 659 # si[impl stdlib.iter.unzip] 660 ## Vec-specific unzip: each element's `.a` / `.b` go onto matching Vecs. 661 .unzip ( (Vec (Pair a b)) -> (Pair (Vec a) (Vec b)) ) 662 (Vec .default) (Vec .default) Pair [ pop-> e pop-> acc 663 acc .a pop-> aa 664 acc .b pop-> bb 665 aa e .a .push 666 bb e .b .push 667 Pair 668 ] .fold ; 669:end 670 671:impl Foldable (HashMap key val) 672 .fold ( (HashMap key val) acc [acc (Pair key val) -> acc] -> acc ) 673 pop-> f pop-> acc pop-> map 0 pop-> i 674 :loop i map .len = :if acc :ret :end 675 acc i map hashmap-entry-at Pair f .call pop-> acc 676 i 1 + pop-> i 677 :end ; 678 .for-each ( (HashMap key val) [(Pair key val) ->] -> ) 679 pop-> f pop-> map 0 pop-> i 680 :loop i map .len = :if :ret :end 681 i map hashmap-entry-at Pair f .call 682 i 1 + pop-> i 683 :end ; 684:end 685 686:impl Foldable (HashSet elem) 687 .fold ( (HashSet elem) acc [acc elem -> acc] -> acc ) 688 pop-> f pop-> acc pop-> set 0 pop-> i 689 :loop i set .len = :if acc :ret :end 690 acc i set hashset-entry-at f .call pop-> acc 691 i 1 + pop-> i 692 :end ; 693 .for-each ( (HashSet elem) [elem ->] -> ) 694 pop-> f pop-> set 0 pop-> i 695 :loop i set .len = :if :ret :end 696 i set hashset-entry-at f .call 697 i 1 + pop-> i 698 :end ; 699:end 700 701# si[impl coll.range.foldable] 702:impl Foldable (RangeBoth a b) 703 .fold ( (RangeBoth AnyInt AnyInt) acc [acc AnyInt -> acc] -> acc ) 704 range-fold ; 705 .for-each ( (RangeBoth AnyInt AnyInt) [AnyInt ->] -> ) 706 range-for-each ; 707:end 708 709# si[impl coll.mappable] 710## Trait for collections whose elements can be transformed element-wise. 711## The `mapped` associated type determines the output collection so that 712## length-preserving maps over `Vec n` can preserve `n` while maps over 713## `HashSet` fall back to a `Vec`. 714:trait(pub) Mappable | mapped 715 ## Apply `f` to each element, collecting results into `(mapped b)`. 716 ## ( (Self a) [a -> b] -> (mapped b) ) 717 .map ( (Self a) [a -> b] -> (mapped b) ) 718:end 719 720:impl Mappable (Vec a len) | (Vec b len) 721 .map ( (Vec a len) [a -> b] -> (Vec b len) ) 722 pop-> f pop-> vec (Vec .default) pop-> out 0 pop-> i 723 :loop i vec .len = :if out :ret :end 724 i vec .get f .call out swap .push pop-> out 725 i 1 + pop-> i 726 :end ; 727:end 728 729# si[impl coll.filterable] 730## Trait for collections that support element selection by predicate. 731## The `filtered` associated type determines the output collection so 732## that `HashSet` filters return a `HashSet` while `Vec` filters return 733## a length-existential `Vec _`. 734:trait(pub) Filterable | filtered 735 ## Retain only elements for which `pred` returns `true`. 736 ## ( (Self elem) [elem -> Bool] -> filtered ) 737 .filter ( (Self elem) [elem -> Bool] -> filtered ) 738:end 739 740:impl Filterable (Vec elem len) | (Vec elem len) 741 .filter ( (Vec elem len) [elem -> Bool] -> (Vec elem len) ) 742 pop-> pred pop-> vec (Vec .default) pop-> out 0 pop-> i 743 :loop i vec .len = :if out :ret :end 744 i vec .get pop-> e 745 e pred .call :if out e .push :else out :end pop-> out 746 i 1 + pop-> i 747 :end ; 748:end 749 750# si[impl coll.keyed] 751## Trait for collections that support key-indexed access. 752## Independent of iteration order — does not require `Foldable`. 753:trait(pub) Keyed 754 ## Return `Some val` if `key` is present, `None` otherwise. 755 ## ( key (Self key val) -> (Option val) ) 756 .lookup ( key (Self key val) -> (Option val) ) 757 ## Return `true` if `key` is present in the collection. 758 ## ( key (Self key val) -> Bool ) 759 .contains-key ( key (Self key val) -> Bool ) 760:end 761 762:impl Keyed (HashMap key val) 763 .lookup ( key (HashMap key val) -> (Option val) ) 764 .get :if Some :else drop None :end ; 765 .contains-key ( key (HashMap key val) -> Bool ) 766 .get swap drop ; 767:end 768 769# si[impl coll.chainable] 770## Trait for collections that can be concatenated end-to-end. 771:trait(pub) Chainable 772 ## Produce a single collection containing every element of the first 773 ## followed by every element of the second, in iteration order. 774 ## ( (Self elem) (Self elem) -> (Self elem) ) 775 .chain ( (Self elem) (Self elem) -> (Self elem) ) 776:end 777 778:impl Chainable (Vec elem len) 779 .chain ( (Vec elem len) (Vec elem len) -> (Vec elem len) ) 780 pop-> v2 pop-> v1 v1 pop-> out 0 pop-> i 781 :loop i v2 .len = :if out :ret :end 782 out i v2 .get .push pop-> out 783 i 1 + pop-> i 784 :end ; 785:end 786 787# si[impl coll.indexed] 788## Trait for collections with positional access by integer index. 789:trait(pub) Indexed 790 ## Return `Some elem` at zero-based index `i`, or `None` if `i` is out 791 ## of bounds. 792 ## ( AnyInt (Self elem) -> (Option elem) ) 793 .at ( AnyInt (Self elem) -> (Option elem) ) 794 ## Return the number of elements in the collection. 795 ## ( (Self elem) -> AnyInt ) 796 .size ( (Self elem) -> AnyInt ) 797:end 798 799:impl Indexed (Vec elem len) 800 .at ( AnyInt (Vec elem len) -> (Option elem) ) 801 pop-> vec pop-> i 802 i 0 < :if None :ret :end 803 i vec .len ≤ not :if None :ret :end 804 i vec .get Some ; 805 .size ( (Vec elem len) -> AnyInt ) .len ; 806:end 807 808# si[impl coll.zippable] 809## Trait for collections that can be combined element-wise. 810:trait(pub) Zippable 811 ## Pair elements positionally; the result has the length of the 812 ## shorter input. 813 ## ( (Self a) (Self b) -> (Self (Pair a b)) ) 814 .zip ( (Self a) (Self b) -> (Self (Pair a b)) ) 815:end 816 817:impl Zippable (Vec a la) 818 .zip ( (Vec a la) (Vec b lb) -> (Vec (Pair a b) lc) ) 819 pop-> vb pop-> va 820 va .len vb .len < :if va .len :else vb .len :end pop-> n 821 (Vec .default) pop-> out 0 pop-> i 822 :loop i n = :if out :ret :end 823 i va .get i vb .get Pair out swap .push pop-> out 824 i 1 + pop-> i 825 :end ; 826:end 827 828# Generic collection helpers — 829# `any` / `all` / `count` are available as `.any` / `.all` / `.count` 830# methods on the `Foldable` trait (default implementations at 831# si[coll.foldable.default]). Callers should use the method form; 832# the free-fn forms used to exist here as a compatibility surface and 833# were removed when WP-M3 hoisted them back into the trait-default 834# surface they duplicated. 835 836# si[impl stdlib.iter.sum+1] 837## Sum the elements of a `Foldable` collection of AnyInt values. 838## Spec targets a generic `(Add num num | num)` constraint; monomorphise 839## to AnyInt for now since the trait-constrained generic :fn path 840## doesn't specialise (Phase 1b open issue). 841## ( (f AnyInt) -> AnyInt ) { (Foldable f) } 842:fn(pub) sum-int ( (f AnyInt) -> AnyInt ) { (Foldable f) } 843 0 [ + ] .fold 844:end 845 846# si[impl stdlib.iter.product+1] 847## Multiply the elements of a `Foldable` collection of AnyInt values. 848## ( (f AnyInt) -> AnyInt ) { (Foldable f) } 849:fn(pub) product-int ( (f AnyInt) -> AnyInt ) { (Foldable f) } 850 1 [ * ] .fold 851:end 852 853# Collection trait aliases 854## Sequential-collection alias — any type that is foldable, mappable, 855## filterable, chainable, indexed, and zippable. 856:trait(pub) Seq { (Foldable Self) (Mappable Self) (Filterable Self) (Chainable Self) (Indexed Self) (Zippable Self) } :end 857## Key-value-collection alias — any keyed, foldable type. 858:trait(pub) Map { (Keyed Self) (Foldable Self) } :end 859# si[impl coll.set+1] 860## Unique-element collection trait. Requires `Foldable` + `Filterable` 861## and carries the set-algebra methods. Concrete impls (HashSet, 862## BTreeSet) supply the bodies. Unicode operator sugar (`∪`/`∩`/`∖`/ 863## `△`/`⊆`/…) desugars to these calls. 864:trait(pub) Set { (Foldable Self) (Filterable Self) } 865 ## Union of two sets. 866 ## ( (Self a) (Self a) -> (Self a) ) 867 .union ( (Self a) (Self a) -> (Self a) ) 868 ## Intersection of two sets. 869 ## ( (Self a) (Self a) -> (Self a) ) 870 .intersection ( (Self a) (Self a) -> (Self a) ) 871 ## Elements in the first set not in the second. 872 ## ( (Self a) (Self a) -> (Self a) ) 873 .difference ( (Self a) (Self a) -> (Self a) ) 874 ## Elements in either set but not both. 875 ## ( (Self a) (Self a) -> (Self a) ) 876 .symmetric-difference ( (Self a) (Self a) -> (Self a) ) 877 ## `true` iff every element of the first is in the second. 878 ## ( (Self a) (Self a) -> Bool ) 879 .is-subset ( (Self a) (Self a) -> Bool ) 880 ## `true` iff every element of the second is in the first. 881 ## ( (Self a) (Self a) -> Bool ) 882 .is-superset ( (Self a) (Self a) -> Bool ) 883 ## `true` iff the two sets share no elements. 884 ## ( (Self a) (Self a) -> Bool ) 885 .is-disjoint ( (Self a) (Self a) -> Bool ) 886 ## `true` iff `a` is an element of the set. 887 ## ( (Self a) a -> Bool ) 888 .contains ( (Self a) a -> Bool ) 889:end