1/**
2 * Works with JavaScript values that implement `[Symbol.iterator]`.
3 *
4 * Iterables include arrays, strings, generators, sets, and custom lazy
5 * sequences. The helpers in this module let code transform, search, group, and
6 * fold iterable values while preserving the input as an iterable instead of
7 * forcing an array first.
8 *
9 * @since 2.0.0
10 */
11
12import type { NonEmptyArray } from "./Array.ts"
13import * as Equal from "./Equal.ts"
14import { dual } from "./Function.ts"
15import * as Count from "./internal/count.ts"
16import * as InternalRecord from "./internal/record.ts"
17import type { Option } from "./Option.ts"
18import * as O from "./Option.ts"
19import { isBoolean } from "./Predicate.ts"
20import type * as Record from "./Record.ts"
21import type { Result } from "./Result.ts"
22import * as R from "./Result.ts"
23import * as Tuple from "./Tuple.ts"
24import type { NoInfer } from "./Types.ts"
25
26/**
27 * Creates an iterable by applying a function to consecutive integers.
28 *
29 * **Details**
30 *
31 * The function is called with each index starting from `0`. If a length is
32 * provided, it is rounded down and normalized to at least `1`, with `NaN`
33 * treated as `1`. If no length is specified, the iterable is infinite.
34 *
35 * **Example** (Generating values by index)
36 *
37 * ```ts import.meta.vitest
38 * import { Iterable } from "effect"
39 *
40 * // Generate first 5 even numbers
41 * const evens = Iterable.makeBy((n) => n * 2, { length: 5 })
42 * Array.from(evens) // => [0, 2, 4, 6, 8]
43 *
44 * // Generate squares
45 * const squares = Iterable.makeBy((n) => n * n, { length: 4 })
46 * Array.from(squares) // => [0, 1, 4, 9]
47 *
48 * // Infinite sequence (be careful when consuming!)
49 * const naturals = Iterable.makeBy((n) => n)
50 * const first10 = Iterable.take(naturals, 10)
51 * Array.from(first10) // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
52 * ```
53 *
54 * @category constructors
55 * @since 2.0.0
56 */
57export const makeBy = <A>(f: (i: number) => A, options?: {
58 readonly length?: number
59}): Iterable<A> => {
60 const max = options?.length !== undefined ? Count.normalizeNonEmpty(options.length) : Infinity
61 return {
62 [Symbol.iterator]() {
63 let i = 0
64 return {
65 next(): IteratorResult<A> {
66 if (i < max) {
67 return { value: f(i++), done: false }
68 }
69 return { done: true, value: undefined }
70 }
71 }
72 }
73 }
74}
75
76/**
77 * Returns an iterable of integers starting at `start` and increasing by `1`.
78 *
79 * **Details**
80 *
81 * When `end` is provided and `start <= end`, both endpoints are included. When
82 * `end` is omitted, the iterable is unbounded. When `start > end`, the
83 * iterable contains only `start`.
84 *
85 * **Example** (Creating a range)
86 *
87 * ```ts import.meta.vitest
88 * import { Iterable } from "effect"
89 *
90 * Array.from(Iterable.range(1, 3)) // => [1, 2, 3]
91 * ```
92 *
93 * @category constructors
94 * @since 2.0.0
95 */
96export const range = (start: number, end?: number): Iterable<number> => {
97 if (end === undefined) {
98 return makeBy((i) => start + i)
99 }
100 return makeBy((i) => start + i, {
101 length: start <= end ? end - start + 1 : 1
102 })
103}
104
105/**
106 * Returns a `Iterable` containing a value repeated the specified number of times.
107 *
108 * **Details**
109 *
110 * `n` is rounded down and normalized to an integer greater than or equal to
111 * `1`. `NaN` is treated as `1`.
112 *
113 * **Example** (Repeating a value)
114 *
115 * ```ts import.meta.vitest
116 * import { Iterable } from "effect"
117 *
118 * Array.from(Iterable.replicate("a", 3)) // => ["a", "a", "a"]
119 * ```
120 *
121 * @category constructors
122 * @since 2.0.0
123 */
124export const replicate: {
125 (n: number): <A>(a: A) => Iterable<A>
126 <A>(a: A, n: number): Iterable<A>
127} = dual(2, <A>(a: A, n: number): Iterable<A> => makeBy(() => a, { length: n }))
128
129/**
130 * Repeats an iterable `n` times, yielding the full contents of `self` for each
131 * repetition.
132 *
133 * **When to use**
134 *
135 * Use to repeat an iterable's contents a specific number of times.
136 *
137 * **Details**
138 *
139 * The result is lazy. `n` is rounded down and normalized to at least `1`, with
140 * `NaN` treated as `1`. Each repetition obtains a new iterator from `self`.
141 *
142 * @see {@link forever} for repeating without an upper bound
143 * @see {@link replicate} for repeating a single value
144 * @category constructors
145 * @since 4.0.0
146 */
147export const repeat: {
148 (n: number): <A>(self: Iterable<A>) => Iterable<A>
149 <A>(self: Iterable<A>, n: number): Iterable<A>
150} = dual(2, <A>(self: Iterable<A>, n: number): Iterable<A> => flatten(makeBy(() => self, { length: n })))
151
152/**
153 * Repeats an iterable without an upper bound.
154 *
155 * **When to use**
156 *
157 * Use to cycle a reusable iterable without an upper bound when a downstream
158 * consumer controls how many values are taken.
159 *
160 * **Gotchas**
161 *
162 * The returned iterable is lazy and should usually be bounded with `take` or
163 * another terminating consumer before materializing it.
164 *
165 * @see {@link repeat} for repeating an iterable a specific number of times
166 * @see {@link take} for bounding the unbounded result before materializing it
167 *
168 * @category constructors
169 * @since 4.0.0
170 */
171export const forever = <A>(self: Iterable<A>): Iterable<A> => repeat(self, Infinity)
172
173/**
174 * Takes a record and returns an Iterable of tuples containing its keys and values.
175 *
176 * **Example** (Converting a record to entries)
177 *
178 * ```ts import.meta.vitest
179 * import { Iterable } from "effect"
180 *
181 * const x = { a: 1, b: 2, c: 3 }
182 * Array.from(Iterable.fromRecord(x)) // => [["a", 1], ["b", 2], ["c", 3]]
183 * ```
184 *
185 * @category converting
186 * @since 2.0.0
187 */
188export const fromRecord = <K extends string, A>(self: Readonly<Record<K, A>>): Iterable<[K, A]> => ({
189 *[Symbol.iterator]() {
190 for (const key in self) {
191 if (Object.hasOwn(self, key)) {
192 yield [key, self[key]]
193 }
194 }
195 }
196})
197
198/**
199 * Prepends an element to the front of an `Iterable`, creating a new `Iterable`.
200 *
201 * **Example** (Prepending an element)
202 *
203 * ```ts import.meta.vitest
204 * import { Iterable } from "effect"
205 *
206 * const numbers = [2, 3, 4]
207 * const withOne = Iterable.prepend(numbers, 1)
208 * Array.from(withOne) // => [1, 2, 3, 4]
209 *
210 * // Works with any iterable
211 * const letters = "abc"
212 * const withZ = Iterable.prepend(letters, "z")
213 * Array.from(withZ) // => ["z", "a", "b", "c"]
214 * ```
215 *
216 * @category combining
217 * @since 2.0.0
218 */
219export const prepend: {
220 <B>(head: B): <A>(self: Iterable<A>) => Iterable<A | B>
221 <A, B>(self: Iterable<A>, head: B): Iterable<A | B>
222} = dual(2, <A, B>(self: Iterable<A>, head: B): Iterable<A | B> => prependAll(self, [head]))
223
224/**
225 * Prepends the specified prefix iterable to the beginning of the specified iterable.
226 *
227 * **Example** (Prepending another iterable)
228 *
229 * ```ts import.meta.vitest
230 * import { Iterable } from "effect"
231 *
232 * Array.from(Iterable.prependAll([1, 2], ["a", "b"])) // => ["a", "b", 1, 2]
233 * ```
234 *
235 * @category combining
236 * @since 2.0.0
237 */
238export const prependAll: {
239 <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<A | B>
240 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>
241} = dual(
242 2,
243 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B> => appendAll(that, self)
244)
245
246/**
247 * Appends an element to the end of an `Iterable`, creating a new `Iterable`.
248 *
249 * **When to use**
250 *
251 * Use to add one element after all elements of an iterable while keeping the
252 * result as a lazy `Iterable`.
253 *
254 * **Details**
255 *
256 * The result yields every element from `self` first, then yields `last` after
257 * `self` is exhausted.
258 *
259 * **Gotchas**
260 *
261 * If `self` is infinite or never completes, the appended element is never
262 * reached.
263 *
264 * **Example** (Appending an element)
265 *
266 * ```ts import.meta.vitest
267 * import { Iterable } from "effect"
268 *
269 * const numbers = [1, 2, 3]
270 * Array.from(Iterable.append(numbers, 4)) // => [1, 2, 3, 4]
271 * ```
272 *
273 * @see {@link prepend} for adding one element before the existing elements
274 * @see {@link appendAll} for appending all elements from another iterable
275 *
276 * @category combining
277 * @since 2.0.0
278 */
279export const append: {
280 <B>(last: B): <A>(self: Iterable<A>) => Iterable<A | B>
281 <A, B>(self: Iterable<A>, last: B): Iterable<A | B>
282} = dual(2, <A, B>(self: Iterable<A>, last: B): Iterable<A | B> => appendAll(self, [last]))
283
284/**
285 * Concatenates two iterables, combining their elements.
286 *
287 * **When to use**
288 *
289 * Use to lazily concatenate two iterables while preserving order, yielding all
290 * elements from `self` before `that`.
291 *
292 * **Details**
293 *
294 * The result is lazy. The iterator for `that` is not created or read until
295 * `self` is exhausted.
296 *
297 * **Gotchas**
298 *
299 * If `self` is infinite or never completes, `that` is never reached.
300 *
301 * **Example** (Concatenating iterables)
302 *
303 * ```ts import.meta.vitest
304 * import { Iterable } from "effect"
305 *
306 * Array.from(Iterable.appendAll([1, 2, 3], [4, 5, 6])) // => [1, 2, 3, 4, 5, 6]
307 *
308 * // Works with different iterable types
309 * const numbers = [1, 2]
310 * const letters = "abc"
311 * const mixed = Iterable.appendAll(numbers, letters)
312 * Array.from(mixed) // => [1, 2, "a", "b", "c"]
313 *
314 * // Lazy evaluation - only consumes what's needed
315 * const infinite = Iterable.range(1)
316 * const finite = [0, -1, -2]
317 * Array.from(Iterable.take(Iterable.appendAll(finite, infinite), 5)) // => [0, -1, -2, 1, 2]
318 * ```
319 *
320 * @see {@link append} for appending one value instead of another iterable
321 * @see {@link prependAll} for yielding another iterable before `self`
322 *
323 * @category combining
324 * @since 2.0.0
325 */
326export const appendAll: {
327 <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<A | B>
328 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B>
329} = dual(
330 2,
331 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<A | B> => ({
332 [Symbol.iterator]() {
333 const iterA = self[Symbol.iterator]()
334 let doneA = false
335 let iterB: Iterator<B>
336 return {
337 next() {
338 if (!doneA) {
339 const r = iterA.next()
340 if (r.done) {
341 doneA = true
342 iterB = that[Symbol.iterator]()
343 return iterB.next()
344 }
345 return r
346 }
347 return iterB.next()
348 }
349 }
350 }
351 })
352)
353
354/**
355 * Reduces an `Iterable` from the left, keeping all intermediate results instead of only the final result.
356 *
357 * **Example** (Tracking running results)
358 *
359 * ```ts import.meta.vitest
360 * import { Iterable } from "effect"
361 *
362 * // Running sum of numbers
363 * const numbers = [1, 2, 3, 4, 5]
364 * const runningSum = Iterable.scan(numbers, 0, (acc, n) => acc + n)
365 * Array.from(runningSum) // => [0, 1, 3, 6, 10, 15]
366 *
367 * // Build strings progressively
368 * const letters = ["a", "b", "c"]
369 * const progressive = Iterable.scan(letters, "", (acc, letter) => acc + letter)
370 * Array.from(progressive) // => ["", "a", "ab", "abc"]
371 *
372 * // Track maximum values seen so far
373 * const values = [3, 1, 4, 1, 5, 9, 2]
374 * const runningMax = Iterable.scan(values, -Infinity, Math.max)
375 * Array.from(runningMax) // => [-Infinity, 3, 3, 4, 4, 5, 9, 9]
376 * ```
377 *
378 * @category folding
379 * @since 2.0.0
380 */
381export const scan: {
382 <B, A>(b: B, f: (b: B, a: A) => B): (self: Iterable<A>) => Iterable<B>
383 <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A) => B): Iterable<B>
384} = dual(3, <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A) => B): Iterable<B> => ({
385 [Symbol.iterator]() {
386 let acc = b
387 let iterator: Iterator<A> | undefined
388 function next() {
389 if (iterator === undefined) {
390 iterator = self[Symbol.iterator]()
391 return { done: false, value: acc }
392 }
393 const result = iterator.next()
394 if (result.done) {
395 return result
396 }
397 acc = f(acc, result.value)
398 return { done: false, value: acc }
399 }
400 return { next }
401 }
402}))
403
404/**
405 * Checks whether an `Iterable` is empty.
406 *
407 * **Example** (Checking for emptiness)
408 *
409 * ```ts import.meta.vitest
410 * import { Iterable } from "effect"
411 *
412 * Iterable.isEmpty([]) // => true
413 * Iterable.isEmpty([1, 2, 3]) // => false
414 * ```
415 *
416 * @category guards
417 * @since 2.0.0
418 */
419export const isEmpty = <A>(self: Iterable<A>): self is Iterable<never> => {
420 const iterator = self[Symbol.iterator]()
421 return iterator.next().done === true
422}
423
424/**
425 * Returns the number of elements in a `Iterable`.
426 *
427 * **Example** (Counting iterable elements)
428 *
429 * ```ts import.meta.vitest
430 * import { Iterable } from "effect"
431 *
432 * const numbers = [1, 2, 3, 4, 5]
433 * Iterable.size(numbers) // => 5
434 *
435 * const empty = Iterable.empty<number>()
436 * Iterable.size(empty) // => 0
437 *
438 * // Works with any iterable
439 * const letters = "hello"
440 * Iterable.size(letters) // => 5
441 *
442 * // Note: This consumes the entire iterable
443 * const range = Iterable.range(1, 100)
444 * Iterable.size(range) // => 100
445 * ```
446 *
447 * @category getters
448 * @since 2.0.0
449 */
450export const size = <A>(self: Iterable<A>): number => {
451 const iterator = self[Symbol.iterator]()
452 let count = 0
453 while (!iterator.next().done) {
454 count++
455 }
456 return count
457}
458
459/**
460 * Gets the first element of a `Iterable` safely, or `None` if the `Iterable` is empty.
461 *
462 * **Example** (Getting the first element)
463 *
464 * ```ts import.meta.vitest
465 * import { Iterable, Option } from "effect"
466 *
467 * const numbers = [1, 2, 3]
468 * Iterable.head(numbers) // => Option.some(1)
469 *
470 * const empty = Iterable.empty<number>()
471 * Iterable.head(empty) // => Option.none()
472 *
473 * // Safe way to get first element
474 * const firstEven = Iterable.head(
475 * Iterable.filter([1, 3, 4, 5], (x) => x % 2 === 0)
476 * )
477 * firstEven // => Option.some(4)
478 *
479 * // Use with Option methods
480 * const doubled = Option.map(Iterable.head([5, 10, 15]), (x) => x * 2)
481 * doubled // => Option.some(10)
482 * ```
483 *
484 * @category getters
485 * @since 2.0.0
486 */
487export const head = <A>(self: Iterable<A>): Option<A> => {
488 const iterator = self[Symbol.iterator]()
489 const result = iterator.next()
490 return result.done ? O.none() : O.some(result.value)
491}
492
493/**
494 * Gets the first element of an `Iterable` without returning an `Option`.
495 *
496 * **When to use**
497 *
498 * Use when the `Iterable` is known to be non-empty and direct access to the
499 * first element is preferred over handling `Option.none`.
500 *
501 * **Gotchas**
502 *
503 * Throws if the `Iterable` is empty.
504 *
505 * **Example** (Getting the first element unsafely)
506 *
507 * ```ts import.meta.vitest
508 * import { Iterable } from "effect"
509 *
510 * const numbers = [1, 2, 3]
511 * Iterable.headUnsafe(numbers) // => 1
512 *
513 * const letters = "hello"
514 * Iterable.headUnsafe(letters) // => "h"
515 *
516 * // Iterable.headUnsafe(Iterable.empty<number>())
517 * // throws Error: "headUnsafe: empty iterable"
518 *
519 * // Use only when you're certain the iterable is non-empty
520 * const nonEmpty = Iterable.range(1, 10)
521 * Iterable.headUnsafe(nonEmpty) // => 1
522 * ```
523 *
524 * @category getters
525 * @since 4.0.0
526 */
527export const headUnsafe = <A>(self: Iterable<A>): A => {
528 const iterator = self[Symbol.iterator]()
529 const result = iterator.next()
530 if (result.done) throw new Error("headUnsafe: empty iterable")
531 return result.value
532}
533
534/**
535 * Keeps only a max number of elements from the start of an `Iterable`, creating a new `Iterable`.
536 *
537 * **Details**
538 *
539 * `n` is rounded down and normalized to a non-negative integer. `NaN` is
540 * treated as `0`.
541 *
542 * **Example** (Taking from the start)
543 *
544 * ```ts import.meta.vitest
545 * import { Iterable } from "effect"
546 *
547 * const numbers = [1, 2, 3, 4, 5]
548 * const firstThree = Iterable.take(numbers, 3)
549 * Array.from(firstThree) // => [1, 2, 3]
550 *
551 * // Taking more than available returns all elements
552 * const firstTen = Iterable.take(numbers, 10)
553 * Array.from(firstTen) // => [1, 2, 3, 4, 5]
554 *
555 * // Taking 0 or negative returns empty
556 * const none = Iterable.take(numbers, 0)
557 * Array.from(none) // => []
558 *
559 * // Useful with infinite iterables
560 * const naturals = Iterable.range(1)
561 * const firstFive = Iterable.take(naturals, 5)
562 * Array.from(firstFive) // => [1, 2, 3, 4, 5]
563 * ```
564 *
565 * @category getters
566 * @since 2.0.0
567 */
568export const take: {
569 (n: number): <A>(self: Iterable<A>) => Iterable<A>
570 <A>(self: Iterable<A>, n: number): Iterable<A>
571} = dual(2, <A>(self: Iterable<A>, n: number): Iterable<A> => {
572 const count = Count.normalize(n)
573 return {
574 [Symbol.iterator]() {
575 let i = 0
576 const iterator = self[Symbol.iterator]()
577 return {
578 next() {
579 if (i < count) {
580 i++
581 return iterator.next()
582 }
583 return { done: true, value: undefined }
584 }
585 }
586 }
587 }
588})
589
590/**
591 * Takes the longest initial `Iterable` prefix for which all elements satisfy the
592 * specified predicate.
593 *
594 * **Example** (Taking while a predicate holds)
595 *
596 * ```ts import.meta.vitest
597 * import { Iterable } from "effect"
598 *
599 * const numbers = [2, 4, 6, 8, 3, 10, 12]
600 * const evenPrefix = Iterable.takeWhile(numbers, (x) => x % 2 === 0)
601 * Array.from(evenPrefix) // => [2, 4, 6, 8]
602 *
603 * // With index
604 * const letters = ["a", "b", "c", "d", "e"]
605 * const firstThreeByIndex = Iterable.takeWhile(letters, (_, i) => i < 3)
606 * Array.from(firstThreeByIndex) // => ["a", "b", "c"]
607 *
608 * // Stops at first non-matching element
609 * const mixed = [1, 3, 5, 4, 7, 9]
610 * const oddPrefix = Iterable.takeWhile(mixed, (x) => x % 2 === 1)
611 * Array.from(oddPrefix) // => [1, 3, 5]
612 *
613 * // Type refinement
614 * const values: Array<string | number> = ["a", "b", "c", 1, "d"]
615 * const stringPrefix = Iterable.takeWhile(
616 * values,
617 * (x): x is string => typeof x === "string"
618 * )
619 * Array.from(stringPrefix) // => ["a", "b", "c"]
620 * ```
621 *
622 * @category getters
623 * @since 2.0.0
624 */
625export const takeWhile: {
626 <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Iterable<B>
627 <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Iterable<A>
628 <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Iterable<B>
629 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A>
630} = dual(2, <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A> => ({
631 [Symbol.iterator]() {
632 const iterator = self[Symbol.iterator]()
633 let i = 0
634 return {
635 next() {
636 const result = iterator.next()
637 if (result.done || !predicate(result.value, i++)) {
638 return { done: true, value: undefined }
639 }
640 return result
641 }
642 }
643 }
644}))
645
646/**
647 * Drops a max number of elements from the start of an `Iterable`
648 *
649 * **Details**
650 *
651 * `n` is rounded down and normalized to a non-negative integer. `NaN` is
652 * treated as `0`.
653 *
654 * **Example** (Dropping from the start)
655 *
656 * ```ts import.meta.vitest
657 * import { Iterable } from "effect"
658 *
659 * const numbers = [1, 2, 3, 4, 5]
660 * const withoutFirstTwo = Iterable.drop(numbers, 2)
661 * Array.from(withoutFirstTwo) // => [3, 4, 5]
662 *
663 * // Dropping more than available returns empty
664 * const withoutFirstTen = Iterable.drop(numbers, 10)
665 * Array.from(withoutFirstTen) // => []
666 *
667 * // Dropping 0 or negative returns all elements
668 * const all = Iterable.drop(numbers, 0)
669 * Array.from(all) // => [1, 2, 3, 4, 5]
670 *
671 * // Combine with take for slicing
672 * const slice = Iterable.take(Iterable.drop(numbers, 1), 3)
673 * Array.from(slice) // => [2, 3, 4]
674 * ```
675 *
676 * @category getters
677 * @since 2.0.0
678 */
679export const drop: {
680 (n: number): <A>(self: Iterable<A>) => Iterable<A>
681 <A>(self: Iterable<A>, n: number): Iterable<A>
682} = dual(2, <A>(self: Iterable<A>, n: number): Iterable<A> => {
683 const count = Count.normalize(n)
684 return {
685 [Symbol.iterator]() {
686 const iterator = self[Symbol.iterator]()
687 let i = 0
688 return {
689 next() {
690 while (i < count) {
691 const result = iterator.next()
692 if (result.done) {
693 return { done: true, value: undefined }
694 }
695 i++
696 }
697 return iterator.next()
698 }
699 }
700 }
701 }
702})
703
704/**
705 * Returns the first element that satisfies the specified
706 * predicate, or `None` if no such element exists.
707 *
708 * **Example** (Finding the first match)
709 *
710 * ```ts import.meta.vitest
711 * import { Iterable, Option } from "effect"
712 *
713 * const numbers = [1, 3, 4, 6, 8]
714 * const firstEven = Iterable.findFirst(numbers, (x) => x % 2 === 0)
715 * firstEven // => Option.some(4)
716 *
717 * const firstGreaterThan10 = Iterable.findFirst(numbers, (x) => x > 10)
718 * firstGreaterThan10 // => Option.none()
719 *
720 * // With index
721 * const letters = ["a", "b", "c", "d"]
722 * const atEvenIndex = Iterable.findFirst(letters, (_, i) => i % 2 === 0)
723 * atEvenIndex // => Option.some("a")
724 *
725 * // Type refinement
726 * const mixed: Array<string | number> = [1, "hello", 2, "world"]
727 * const firstString = Iterable.findFirst(
728 * mixed,
729 * (x): x is string => typeof x === "string"
730 * )
731 * firstString // => Option.some("hello")
732 *
733 * // Transform during search
734 * const findSquareRoot = Iterable.findFirst([1, 4, 9, 16], (x) => {
735 * const sqrt = Math.sqrt(x)
736 * return Number.isInteger(sqrt) ? Option.some(sqrt) : Option.none()
737 * })
738 * findSquareRoot // => Option.some(1)
739 * ```
740 *
741 * @category searching
742 * @since 2.0.0
743 */
744export const findFirst: {
745 <A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B>
746 <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Option<B>
747 <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Option<A>
748 <A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B>
749 <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B>
750 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>
751} = dual(
752 2,
753 <A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option<A>)): Option<A> => {
754 let i = 0
755 for (const a of self) {
756 const o = f(a, i)
757 if (isBoolean(o)) {
758 if (o) {
759 return O.some(a)
760 }
761 } else {
762 if (O.isSome(o)) {
763 return o
764 }
765 }
766 i++
767 }
768 return O.none()
769 }
770)
771
772/**
773 * Finds the last element for which a predicate holds.
774 *
775 * **Example** (Finding the last match)
776 *
777 * ```ts import.meta.vitest
778 * import { Iterable, Option } from "effect"
779 *
780 * const numbers = [1, 3, 4, 6, 8, 2]
781 * const lastEven = Iterable.findLast(numbers, (x) => x % 2 === 0)
782 * lastEven // => Option.some(2)
783 *
784 * const lastGreaterThan10 = Iterable.findLast(numbers, (x) => x > 10)
785 * lastGreaterThan10 // => Option.none()
786 *
787 * // With index
788 * const letters = ["a", "b", "c", "d", "e"]
789 * const lastAtEvenIndex = Iterable.findLast(letters, (_, i) => i % 2 === 0)
790 * lastAtEvenIndex // => Option.some("e")
791 *
792 * // Type refinement
793 * const mixed: Array<string | number> = [1, "hello", 2, "world", 3]
794 * const lastString = Iterable.findLast(
795 * mixed,
796 * (x): x is string => typeof x === "string"
797 * )
798 * lastString // => Option.some("world")
799 * ```
800 *
801 * @category searching
802 * @since 2.0.0
803 */
804export const findLast: {
805 <A, B>(f: (a: NoInfer<A>, i: number) => Option<B>): (self: Iterable<A>) => Option<B>
806 <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Option<B>
807 <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Option<A>
808 <A, B>(self: Iterable<A>, f: (a: A, i: number) => Option<B>): Option<B>
809 <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Option<B>
810 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Option<A>
811} = dual(
812 2,
813 <A>(self: Iterable<A>, f: ((a: A, i: number) => boolean) | ((a: A, i: number) => Option<A>)): Option<A> => {
814 let i = 0
815 let last: Option<A> = O.none()
816 for (const a of self) {
817 const o = f(a, i)
818 if (isBoolean(o)) {
819 if (o) {
820 last = O.some(a)
821 }
822 } else {
823 if (O.isSome(o)) {
824 last = o
825 }
826 }
827 i++
828 }
829 return last
830 }
831)
832
833/**
834 * Takes two `Iterable`s and returns an `Iterable` of corresponding pairs.
835 *
836 * **Example** (Zipping iterables)
837 *
838 * ```ts import.meta.vitest
839 * import { Iterable } from "effect"
840 *
841 * const numbers = [1, 2, 3]
842 * const letters = ["a", "b", "c"]
843 * const zipped = Iterable.zip(numbers, letters)
844 * Array.from(zipped) // => [[1, "a"], [2, "b"], [3, "c"]]
845 *
846 * // Different lengths - shorter one determines result length
847 * const short = [1, 2]
848 * const long = ["a", "b", "c", "d"]
849 * const partial = Iterable.zip(short, long)
850 * Array.from(partial) // => [[1, "a"], [2, "b"]]
851 *
852 * // Works with any iterables
853 * const range = Iterable.range(1, 3)
854 * const word = "abc"
855 * const mixed = Iterable.zip(range, word)
856 * Array.from(mixed) // => [[1, "a"], [2, "b"], [3, "c"]]
857 *
858 * // Create indexed pairs
859 * const values = ["apple", "banana", "cherry"]
860 * const indices = Iterable.range(0, 2)
861 * const indexed = Iterable.zip(indices, values)
862 * Array.from(indexed) // => [[0, "apple"], [1, "banana"], [2, "cherry"]]
863 * ```
864 *
865 * @category zipping
866 * @since 2.0.0
867 */
868export const zip: {
869 <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<[A, B]>
870 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]>
871} = dual(
872 2,
873 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]> => zipWith(self, that, Tuple.make)
874)
875
876/**
877 * Applies a function to pairs of elements at the same index in two `Iterable`s, collecting the results. If one
878 * input `Iterable` is short, excess elements of the longer `Iterable` are discarded.
879 *
880 * **Example** (Zipping with a combining function)
881 *
882 * ```ts import.meta.vitest
883 * import { Iterable } from "effect"
884 *
885 * // Add corresponding elements
886 * const a = [1, 2, 3, 4]
887 * const b = [10, 20, 30, 40]
888 * const sums = Iterable.zipWith(a, b, (x, y) => x + y)
889 * Array.from(sums) // => [11, 22, 33, 44]
890 *
891 * // Combine strings
892 * const firstNames = ["John", "Jane", "Bob"]
893 * const lastNames = ["Doe", "Smith", "Johnson"]
894 * const fullNames = Iterable.zipWith(
895 * firstNames,
896 * lastNames,
897 * (first, last) => `${first} ${last}`
898 * )
899 * Array.from(fullNames) // => ["John Doe", "Jane Smith", "Bob Johnson"]
900 *
901 * // Different lengths - stops at shorter
902 * const short = [1, 2]
903 * const long = ["a", "b", "c", "d"]
904 * const combined = Iterable.zipWith(
905 * short,
906 * long,
907 * (num, letter) => `${num}${letter}`
908 * )
909 * Array.from(combined) // => ["1a", "2b"]
910 *
911 * // Complex transformations
912 * const prices = [10.99, 25.50, 5.00]
913 * const quantities = [2, 1, 3]
914 * const totals = Iterable.zipWith(prices, quantities, (price, qty) => {
915 * return Math.round(price * qty * 100) / 100 // round to 2 decimal places
916 * })
917 * Array.from(totals) // => [21.98, 25.5, 15]
918 * ```
919 *
920 * @category zipping
921 * @since 2.0.0
922 */
923export const zipWith: {
924 <B, A, C>(that: Iterable<B>, f: (a: A, b: B) => C): (self: Iterable<A>) => Iterable<C>
925 <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C>
926} = dual(3, <B, A, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C> => ({
927 [Symbol.iterator]() {
928 const selfIterator = self[Symbol.iterator]()
929 const thatIterator = that[Symbol.iterator]()
930 return {
931 next() {
932 const selfResult = selfIterator.next()
933 const thatResult = thatIterator.next()
934 if (selfResult.done || thatResult.done) {
935 return { done: true, value: undefined }
936 }
937 return { done: false, value: f(selfResult.value, thatResult.value) }
938 }
939 }
940 }
941}))
942
943/**
944 * Places a separator between members of an `Iterable`.
945 *
946 * **When to use**
947 *
948 * Use to lazily insert a separator between adjacent values.
949 *
950 * **Details**
951 *
952 * If the input is a non-empty array, the result is also a non-empty array.
953 *
954 * **Example** (Interspersing separators)
955 *
956 * ```ts import.meta.vitest
957 * import { Iterable } from "effect"
958 *
959 * // Join numbers with separator
960 * const numbers = [1, 2, 3, 4]
961 * const withCommas = Iterable.intersperse(numbers, ",")
962 * Array.from(withCommas) // => [1, ",", 2, ",", 3, ",", 4]
963 *
964 * // Join words with spaces
965 * const words = ["hello", "world", "from", "effect"]
966 * const sentence = Iterable.intersperse(words, " ")
967 * Array.from(sentence).join("") // => "hello world from effect"
968 *
969 * // Empty iterable remains empty
970 * const empty = Iterable.empty<string>()
971 * const stillEmpty = Iterable.intersperse(empty, "-")
972 * Array.from(stillEmpty) // => []
973 *
974 * // Single element has no separators added
975 * const single = [42]
976 * const noSeparator = Iterable.intersperse(single, "|")
977 * Array.from(noSeparator) // => [42]
978 *
979 * // Build CSS-like strings
980 * const styles = ["color: red", "font-size: 14px", "margin: 10px"]
981 * const css = Iterable.intersperse(styles, "; ")
982 * Array.from(css).join("") // => "color: red; font-size: 14px; margin: 10px"
983 * ```
984 *
985 * @category combining
986 * @since 2.0.0
987 */
988export const intersperse: {
989 <B>(middle: B): <A>(self: Iterable<A>) => Iterable<A | B>
990 <A, B>(self: Iterable<A>, middle: B): Iterable<A | B>
991} = dual(2, <A, B>(self: Iterable<A>, middle: B): Iterable<A | B> => ({
992 [Symbol.iterator]() {
993 const iterator = self[Symbol.iterator]()
994 let next = iterator.next()
995 let emitted = false
996 return {
997 next() {
998 if (next.done) {
999 return next
1000 } else if (emitted) {
1001 emitted = false
1002 return { done: false, value: middle }
1003 }
1004 emitted = true
1005 const result = next
1006 next = iterator.next()
1007 return result
1008 }
1009 }
1010 }
1011}))
1012
1013/**
1014 * Returns a function that checks if an `Iterable` contains a given value using a provided `isEquivalent` function.
1015 *
1016 * **Example** (Checking membership with custom equivalence)
1017 *
1018 * ```ts import.meta.vitest
1019 * import { Iterable } from "effect"
1020 *
1021 * // Custom equivalence for objects
1022 * const byId = (a: { id: number }, b: { id: number }) => a.id === b.id
1023 * const containsById = Iterable.containsWith(byId)
1024 *
1025 * const users = [{ id: 1 }, { id: 2 }]
1026 * const hasUser1 = containsById(users, { id: 1 })
1027 * hasUser1 // => true
1028 *
1029 * // Case-insensitive string comparison
1030 * const caseInsensitive = (a: string, b: string) =>
1031 * a.toLowerCase() === b.toLowerCase()
1032 * const containsCaseInsensitive = Iterable.containsWith(caseInsensitive)
1033 *
1034 * const words = ["Hello", "World"]
1035 * const hasHello = containsCaseInsensitive(words, "hello")
1036 * hasHello // => true
1037 *
1038 * // Approximate number comparison
1039 * const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1
1040 * const containsApprox = Iterable.containsWith(approxEqual)
1041 *
1042 * const values = [1.0, 2.0, 3.0]
1043 * const hasAlmostTwo = containsApprox(values, 2.05)
1044 * hasAlmostTwo // => true
1045 * ```
1046 *
1047 * @category predicates
1048 * @since 2.0.0
1049 */
1050export const containsWith = <A>(isEquivalent: (self: A, that: A) => boolean): {
1051 (a: A): (self: Iterable<A>) => boolean
1052 (self: Iterable<A>, a: A): boolean
1053} =>
1054 dual(2, (self: Iterable<A>, a: A): boolean => {
1055 for (const i of self) {
1056 if (isEquivalent(a, i)) {
1057 return true
1058 }
1059 }
1060 return false
1061 })
1062
1063/**
1064 * Checks whether an iterable contains a value using Effect's default `Equal`
1065 * equivalence.
1066 *
1067 * **Details**
1068 *
1069 * Can be called as `contains(self, value)` or curried as
1070 * `contains(value)(self)`.
1071 *
1072 * **Example** (Checking membership)
1073 *
1074 * ```ts import.meta.vitest
1075 * import { Iterable } from "effect"
1076 *
1077 * const numbers = [1, 2, 3, 4, 5]
1078 * Iterable.contains(numbers, 3) // => true
1079 * Iterable.contains(numbers, 6) // => false
1080 *
1081 * const letters = "hello"
1082 * Iterable.contains(letters, "l") // => true
1083 * Iterable.contains(letters, "x") // => false
1084 *
1085 * // Works with any iterable
1086 * const range = Iterable.range(1, 100)
1087 * Iterable.contains(range, 50) // => true
1088 * Iterable.contains(range, 150) // => false
1089 *
1090 * // Curried version
1091 * const containsThree = Iterable.contains(3)
1092 * containsThree([1, 2, 3]) // => true
1093 * containsThree([4, 5, 6]) // => false
1094 * ```
1095 *
1096 * @category predicates
1097 * @since 2.0.0
1098 */
1099export const contains: {
1100 <A>(a: A): (self: Iterable<A>) => boolean
1101 <A>(self: Iterable<A>, a: A): boolean
1102} = containsWith(Equal.asEquivalence())
1103
1104/**
1105 * Splits an `Iterable` into length-`n` pieces. The last piece will be shorter if `n` does not evenly divide the length of
1106 * the `Iterable`.
1107 * `n` is rounded down and normalized to at least `1`; `NaN` and non-positive
1108 * values therefore produce singleton pieces.
1109 *
1110 * **Example** (Chunking an iterable)
1111 *
1112 * ```ts import.meta.vitest
1113 * import { Iterable } from "effect"
1114 *
1115 * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
1116 * const chunks = Iterable.chunksOf(numbers, 3)
1117 * Array.from(chunks) // => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
1118 *
1119 * // Last chunk can be shorter
1120 * const uneven = [1, 2, 3, 4, 5, 6, 7]
1121 * const chunks2 = Iterable.chunksOf(uneven, 3)
1122 * Array.from(chunks2) // => [[1, 2, 3], [4, 5, 6], [7]]
1123 *
1124 * // Chunk size larger than iterable
1125 * const small = [1, 2]
1126 * const chunks3 = Iterable.chunksOf(small, 5)
1127 * Array.from(chunks3) // => [[1, 2]]
1128 *
1129 * // Process data in batches
1130 * const data = Iterable.range(1, 100)
1131 * const batches = Iterable.chunksOf(data, 10)
1132 * const batchSums = Iterable.map(
1133 * batches,
1134 * (batch) => Iterable.reduce(batch, 0, (sum, n) => sum + n)
1135 * )
1136 * Array.from(Iterable.take(batchSums, 3)) // => [55, 155, 255]
1137 * ```
1138 *
1139 * @category splitting
1140 * @since 2.0.0
1141 */
1142export const chunksOf: {
1143 (n: number): <A>(self: Iterable<A>) => Iterable<Array<A>>
1144 <A>(self: Iterable<A>, n: number): Iterable<Array<A>>
1145} = dual(2, <A>(self: Iterable<A>, n: number): Iterable<Array<A>> => {
1146 const safeN = Count.normalizeNonEmpty(n)
1147 return ({
1148 [Symbol.iterator]() {
1149 let iterator: Iterator<A> | undefined = self[Symbol.iterator]()
1150 return {
1151 next() {
1152 if (iterator === undefined) {
1153 return { done: true, value: undefined }
1154 }
1155
1156 const chunk: Array<A> = []
1157 for (let i = 0; i < safeN; i++) {
1158 const result = iterator.next()
1159 if (result.done) {
1160 iterator = undefined
1161 return chunk.length === 0 ? { done: true, value: undefined } : { done: false, value: chunk }
1162 }
1163 chunk.push(result.value)
1164 }
1165
1166 return { done: false, value: chunk }
1167 }
1168 }
1169 }
1170 })
1171})
1172
1173/**
1174 * Groups equal, consecutive elements of an `Iterable` into `NonEmptyArray`s using the provided `isEquivalent` function.
1175 *
1176 * **Example** (Grouping consecutive elements with custom equivalence)
1177 *
1178 * ```ts import.meta.vitest
1179 * import { Iterable } from "effect"
1180 *
1181 * // Group consecutive equal numbers
1182 * const numbers = [1, 1, 2, 2, 2, 3, 1, 1]
1183 * const grouped = Iterable.groupWith(numbers, (a, b) => a === b)
1184 * Array.from(grouped) // => [[1, 1], [2, 2, 2], [3], [1, 1]]
1185 *
1186 * // Case-insensitive grouping of strings
1187 * const words = ["Apple", "APPLE", "banana", "Banana", "cherry"]
1188 * const caseInsensitive = (a: string, b: string) =>
1189 * a.toLowerCase() === b.toLowerCase()
1190 * const groupedWords = Iterable.groupWith(words, caseInsensitive)
1191 * Array.from(groupedWords) // => [["Apple", "APPLE"], ["banana", "Banana"], ["cherry"]]
1192 *
1193 * // Group by approximate equality
1194 * const floats = [1.1, 1.12, 1.9, 2.01, 2.05, 3.5]
1195 * const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.2
1196 * const groupedFloats = Iterable.groupWith(floats, approxEqual)
1197 * Array.from(groupedFloats) // => [[1.1, 1.12], [1.9, 2.01, 2.05], [3.5]]
1198 *
1199 * // Only groups consecutive elements
1200 * const scattered = [1, 2, 1, 2, 1]
1201 * const scatteredGroups = Iterable.groupWith(scattered, (a, b) => a === b)
1202 * Array.from(scatteredGroups) // => [[1], [2], [1], [2], [1]]
1203 * ```
1204 *
1205 * @category grouping
1206 * @since 2.0.0
1207 */
1208export const groupWith: {
1209 <A>(isEquivalent: (self: A, that: A) => boolean): (self: Iterable<A>) => Iterable<NonEmptyArray<A>>
1210 <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<NonEmptyArray<A>>
1211} = dual(
1212 2,
1213 <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<NonEmptyArray<A>> => ({
1214 [Symbol.iterator]() {
1215 const iterator = self[Symbol.iterator]()
1216 let nextResult: IteratorResult<A> | undefined
1217 return {
1218 next() {
1219 let result: IteratorResult<A>
1220 if (nextResult !== undefined) {
1221 if (nextResult.done) {
1222 return { done: true, value: undefined }
1223 }
1224 result = nextResult
1225 nextResult = undefined
1226 } else {
1227 result = iterator.next()
1228 if (result.done) {
1229 return { done: true, value: undefined }
1230 }
1231 }
1232 const chunk: NonEmptyArray<A> = [result.value]
1233
1234 while (true) {
1235 const next = iterator.next()
1236 if (next.done || !isEquivalent(result.value, next.value)) {
1237 nextResult = next
1238 return { done: false, value: chunk }
1239 }
1240 chunk.push(next.value)
1241 }
1242 }
1243 }
1244 }
1245 })
1246)
1247
1248/**
1249 * Groups equal, consecutive elements of an `Iterable` into `NonEmptyArray`s.
1250 *
1251 * **Example** (Grouping consecutive elements)
1252 *
1253 * ```ts import.meta.vitest
1254 * import { Iterable } from "effect"
1255 *
1256 * const numbers = [1, 1, 2, 2, 2, 3, 1, 1]
1257 * const grouped = Iterable.group(numbers)
1258 * Array.from(grouped) // => [[1, 1], [2, 2, 2], [3], [1, 1]]
1259 *
1260 * const letters = "aabbccaa"
1261 * const groupedLetters = Iterable.group(letters)
1262 * Array.from(groupedLetters) // => [["a", "a"], ["b", "b"], ["c", "c"], ["a", "a"]]
1263 *
1264 * // Works with objects using deep equality
1265 * const objects = [
1266 * { type: "A", value: 1 },
1267 * { type: "A", value: 1 },
1268 * { type: "B", value: 2 },
1269 * { type: "A", value: 1 }
1270 * ]
1271 * const groupedObjects = Iterable.group(objects)
1272 * Array.from(groupedObjects).length // => 3
1273 * // Note: Only consecutive equal objects are grouped together
1274 * ```
1275 *
1276 * @category grouping
1277 * @since 2.0.0
1278 */
1279export const group: <A>(self: Iterable<A>) => Iterable<NonEmptyArray<A>> = groupWith(
1280 Equal.asEquivalence()
1281)
1282
1283/**
1284 * Groups all elements by the string or symbol key returned by `f`.
1285 *
1286 * **Details**
1287 *
1288 * Each property in the returned record contains a non-empty array of elements
1289 * that produced that key. Unlike `group`, matching elements do not need to be
1290 * consecutive.
1291 *
1292 * **Gotchas**
1293 *
1294 * When the key function returns a finite union of string literals or unique
1295 * symbols, the result preserves those keys as optional properties because the
1296 * input may not produce every key. Open `string` and `symbol` key types retain
1297 * their record index signatures.
1298 *
1299 * **Example** (Grouping by a key)
1300 *
1301 * ```ts import.meta.vitest
1302 * import { Iterable } from "effect"
1303 *
1304 * // Group by string length
1305 * const words = ["a", "bb", "ccc", "dd", "eee", "f"]
1306 * const byLength = Iterable.groupBy(words, (word) => word.length.toString())
1307 * byLength // => { "1": ["a", "f"], "2": ["bb", "dd"], "3": ["ccc", "eee"] }
1308 *
1309 * // Group by first letter
1310 * const names = ["Alice", "Bob", "Charlie", "David", "Anna", "Betty"]
1311 * const byFirstLetter = Iterable.groupBy(names, (name) => name[0])
1312 * byFirstLetter // => { A: ["Alice", "Anna"], B: ["Bob", "Betty"], C: ["Charlie"], D: ["David"] }
1313 *
1314 * // Group by category
1315 * const items = [
1316 * { name: "apple", category: "fruit" },
1317 * { name: "carrot", category: "vegetable" },
1318 * { name: "banana", category: "fruit" },
1319 * { name: "broccoli", category: "vegetable" }
1320 * ]
1321 * const byCategory = Iterable.groupBy(items, (item) => item.category)
1322 * Object.keys(byCategory) // => ["fruit", "vegetable"]
1323 *
1324 * // Group numbers by even/odd
1325 * const numbers = [1, 2, 3, 4, 5, 6]
1326 * const evenOdd = Iterable.groupBy(numbers, (n) => n % 2 === 0 ? "even" : "odd")
1327 * evenOdd // => { odd: [1, 3, 5], even: [2, 4, 6] }
1328 * ```
1329 *
1330 * @category grouping
1331 * @since 2.0.0
1332 */
1333export const groupBy: {
1334 <A, K extends string | symbol>(
1335 f: (a: A) => K
1336 ): (self: Iterable<A>) => Record.ReadonlyRecord.GroupByResult<K, NonEmptyArray<A>>
1337 <A, K extends string | symbol>(
1338 self: Iterable<A>,
1339 f: (a: A) => K
1340 ): Record.ReadonlyRecord.GroupByResult<K, NonEmptyArray<A>>
1341} = dual(2, <A, K extends string | symbol>(
1342 self: Iterable<A>,
1343 f: (a: A) => K
1344): Record.ReadonlyRecord.GroupByResult<K, NonEmptyArray<A>> => {
1345 const out: Record<string | symbol, NonEmptyArray<A>> = {}
1346 for (const a of self) {
1347 const k = f(a)
1348 if (Object.hasOwn(out, k)) {
1349 out[k].push(a)
1350 } else {
1351 InternalRecord.assignProperty(out, k, [a])
1352 }
1353 }
1354 return out
1355})
1356
1357const constEmpty: Iterable<never> = {
1358 [Symbol.iterator]() {
1359 return constEmptyIterator
1360 }
1361}
1362const constEmptyIterator: Iterator<never> = {
1363 next() {
1364 return { done: true, value: undefined }
1365 }
1366}
1367
1368/**
1369 * Creates an empty iterable that yields no elements.
1370 *
1371 * **When to use**
1372 *
1373 * Use when you need an empty iterable as a typed "no data" value or a base
1374 * case for iterable operations.
1375 *
1376 * **Example** (Creating an empty iterable)
1377 *
1378 * ```ts import.meta.vitest
1379 * import { Iterable } from "effect"
1380 *
1381 * Array.from(Iterable.empty<string>()) // => []
1382 * ```
1383 *
1384 * @category constructors
1385 * @since 2.0.0
1386 */
1387export const empty = <A = never>(): Iterable<A> => constEmpty
1388
1389/**
1390 * Creates an iterable containing a single element.
1391 *
1392 * **When to use**
1393 *
1394 * Use to wrap a single value in an iterable context so it can be combined
1395 * with other iterable operations.
1396 *
1397 * **Example** (Wrapping a single value)
1398 *
1399 * ```ts import.meta.vitest
1400 * import { Iterable } from "effect"
1401 *
1402 * const single = Iterable.of(42)
1403 * Array.from(single) // => [42]
1404 *
1405 * // Useful for creating homogeneous sequences
1406 * const sequences = [
1407 * Iterable.of("hello"),
1408 * Iterable.range(1, 3),
1409 * Iterable.empty<string>()
1410 * ]
1411 *
1412 * // Can be used with flatMap for conditional inclusion
1413 * const numbers = [1, 2, 3, 4, 5]
1414 * const evensOnly = Iterable.flatMap(
1415 * numbers,
1416 * (n) => n % 2 === 0 ? Iterable.of(n) : Iterable.empty()
1417 * )
1418 * Array.from(evensOnly) // => [2, 4]
1419 * ```
1420 *
1421 * @category constructors
1422 * @since 2.0.0
1423 */
1424export const of = <A>(a: A): Iterable<A> => [a]
1425
1426/**
1427 * Transforms each element of an iterable using a function.
1428 *
1429 * **Details**
1430 *
1431 * This is one of the most fundamental operations for working with iterables.
1432 * It applies a transformation function to each element, creating a new iterable
1433 * with the transformed values. The operation is lazy, so elements are only
1434 * transformed when the iterable is consumed.
1435 *
1436 * **Example** (Mapping elements)
1437 *
1438 * ```ts import.meta.vitest
1439 * import { Iterable } from "effect"
1440 *
1441 * // Transform numbers to their squares
1442 * const numbers = [1, 2, 3, 4, 5]
1443 * const squares = Iterable.map(numbers, (x) => x * x)
1444 * Array.from(squares) // => [1, 4, 9, 16, 25]
1445 *
1446 * // Use index in transformation
1447 * const indexed = Iterable.map(["a", "b", "c"], (char, i) => `${i}: ${char}`)
1448 * Array.from(indexed) // => ["0: a", "1: b", "2: c"]
1449 *
1450 * Array.from(Iterable.map(
1451 * Iterable.map([1, 2, 3], (x) => x * 2),
1452 * (x) => x + 1
1453 * )) // => [3, 5, 7]
1454 * ```
1455 *
1456 * @category mapping
1457 * @since 2.0.0
1458 */
1459export const map: {
1460 <A, B>(
1461 f: (a: NoInfer<A>, i: number) => B
1462 ): (self: Iterable<A>) => Iterable<B>
1463 <A, B>(self: Iterable<A>, f: (a: NoInfer<A>, i: number) => B): Iterable<B>
1464} = dual(2, <A, B>(self: Iterable<A>, f: (a: A, i: number) => B): Iterable<B> => ({
1465 [Symbol.iterator]() {
1466 const iterator = self[Symbol.iterator]()
1467 let i = 0
1468 return {
1469 next() {
1470 const result = iterator.next()
1471 if (result.done) {
1472 return { done: true, value: undefined }
1473 }
1474 return { done: false, value: f(result.value, i++) }
1475 }
1476 }
1477 }
1478}))
1479
1480/**
1481 * Applies a function to each element in an Iterable and returns a new Iterable containing the concatenated mapped elements.
1482 *
1483 * **Example** (Flat mapping iterables)
1484 *
1485 * ```ts import.meta.vitest
1486 * import { Iterable } from "effect"
1487 *
1488 * // Expand each number to a range
1489 * const numbers = [1, 2, 3]
1490 * const expanded = Iterable.flatMap(numbers, (n) => Iterable.range(1, n))
1491 * Array.from(expanded) // => [1, 1, 2, 1, 2, 3]
1492 *
1493 * // Split strings into characters
1494 * const words = ["hi", "bye"]
1495 * const chars = Iterable.flatMap(words, (word) => word)
1496 * Array.from(chars) // => ["h", "i", "b", "y", "e"]
1497 *
1498 * // Conditional expansion with empty iterables
1499 * const values = [1, 2, 3, 4, 5]
1500 * const evenMultiples = Iterable.flatMap(
1501 * values,
1502 * (n) => n % 2 === 0 ? [n, n * 2, n * 3] : []
1503 * )
1504 * Array.from(evenMultiples) // => [2, 4, 6, 4, 8, 12]
1505 *
1506 * // Use index in transformation
1507 * const letters = ["a", "b", "c"]
1508 * const indexed = Iterable.flatMap(
1509 * letters,
1510 * (letter, i) => Iterable.replicate(letter, i + 1)
1511 * )
1512 * Array.from(indexed) // => ["a", "b", "b", "c", "c", "c"]
1513 * ```
1514 *
1515 * @category sequencing
1516 * @since 2.0.0
1517 */
1518export const flatMap: {
1519 <A, B>(
1520 f: (a: NoInfer<A>, i: number) => Iterable<B>
1521 ): (self: Iterable<A>) => Iterable<B>
1522 <A, B>(self: Iterable<A>, f: (a: NoInfer<A>, i: number) => Iterable<B>): Iterable<B>
1523} = dual(
1524 2,
1525 <A, B>(self: Iterable<A>, f: (a: A, i: number) => Iterable<B>): Iterable<B> => flatten(map(self, f))
1526)
1527
1528/**
1529 * Flattens an Iterable of Iterables into a single Iterable
1530 *
1531 * **Example** (Flattening nested iterables)
1532 *
1533 * ```ts import.meta.vitest
1534 * import { Iterable } from "effect"
1535 *
1536 * // Flatten nested arrays
1537 * const nested = [[1, 2], [3, 4], [5, 6]]
1538 * const flat = Iterable.flatten(nested)
1539 * Array.from(flat) // => [1, 2, 3, 4, 5, 6]
1540 *
1541 * // Flatten different iterable types
1542 * const mixed: Array<Iterable<string>> = ["ab", "cd"]
1543 * const flatMixed = Iterable.flatten(mixed)
1544 * Array.from(flatMixed) // => ["a", "b", "c", "d"]
1545 *
1546 * // Flatten deeply nested (only one level)
1547 * const deepNested = [[[1, 2]], [[3, 4]]]
1548 * const oneLevelFlat = Iterable.flatten(deepNested)
1549 * Array.from(oneLevelFlat) // => [[1, 2], [3, 4]]
1550 * // [[1, 2], [3, 4]] (still contains arrays)
1551 *
1552 * // Empty iterables are handled correctly
1553 * const withEmpty = [[1, 2], [], [3, 4], []]
1554 * const flatWithEmpty = Iterable.flatten(withEmpty)
1555 * Array.from(flatWithEmpty) // => [1, 2, 3, 4]
1556 * ```
1557 *
1558 * @category sequencing
1559 * @since 2.0.0
1560 */
1561export const flatten = <A>(self: Iterable<Iterable<A>>): Iterable<A> => ({
1562 [Symbol.iterator]() {
1563 const outerIterator = self[Symbol.iterator]()
1564 let innerIterator: Iterator<A> | undefined
1565 function next() {
1566 while (true) {
1567 if (innerIterator === undefined) {
1568 const next = outerIterator.next()
1569 if (next.done) {
1570 return next
1571 }
1572 innerIterator = next.value[Symbol.iterator]()
1573 }
1574 const result = innerIterator.next()
1575 if (!result.done) {
1576 return result
1577 }
1578 innerIterator = undefined
1579 }
1580 }
1581 return { next }
1582 }
1583})
1584
1585/**
1586 * Transforms elements of an iterable using a function that returns a `Result`, keeping only successful values.
1587 *
1588 * **Details**
1589 *
1590 * This combines mapping and filtering in a single operation. The function is
1591 * applied to each element, and only elements that result in `Result.succeed`
1592 * are included in the result.
1593 *
1594 * **Example** (Filtering and transforming Result values)
1595 *
1596 * ```ts import.meta.vitest
1597 * import { Iterable, Result } from "effect"
1598 *
1599 * // Parse strings to numbers, keeping only valid ones
1600 * const strings = ["1", "2", "invalid", "4", "not-a-number"]
1601 * const numbers = Iterable.filterMap(strings, (s) => {
1602 * const num = parseInt(s)
1603 * return isNaN(num) ? Result.failVoid : Result.succeed(num)
1604 * })
1605 * Array.from(numbers) // => [1, 2, 4]
1606 *
1607 * // Extract specific properties from objects
1608 * const users = [
1609 * { name: "Alice", age: 25, email: "alice@example.com" },
1610 * { name: "Bob", age: 17, email: undefined },
1611 * { name: "Charlie", age: 30, email: "charlie@example.com" },
1612 * { name: "David", age: 16, email: undefined }
1613 * ]
1614 * const adultEmails = Iterable.filterMap(
1615 * users,
1616 * (user) =>
1617 * user.age >= 18 && user.email ? Result.succeed(user.email) : Result.failVoid
1618 * )
1619 * Array.from(adultEmails) // => ["alice@example.com", "charlie@example.com"]
1620 *
1621 * // Use index in transformation
1622 * const items = ["a", "b", "c", "d", "e"]
1623 * const evenIndexItems = Iterable.filterMap(
1624 * items,
1625 * (item, i) => i % 2 === 0 ? Result.succeed(`${i}: ${item}`) : Result.failVoid
1626 * )
1627 * Array.from(evenIndexItems) // => ["0: a", "2: c", "4: e"]
1628 * ```
1629 *
1630 * @category filtering
1631 * @since 2.0.0
1632 */
1633export const filterMap: {
1634 <A, B, X>(f: (input: A, i: number) => Result<B, X>): (self: Iterable<A>) => Iterable<B>
1635 <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>): Iterable<B>
1636} = dual(
1637 2,
1638 <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>): Iterable<B> => ({
1639 [Symbol.iterator]() {
1640 const iterator = self[Symbol.iterator]()
1641 let i = 0
1642 return {
1643 next() {
1644 let result = iterator.next()
1645 while (!result.done) {
1646 const next = f(result.value, i++)
1647 if (R.isSuccess(next)) {
1648 return { done: false, value: next.success }
1649 }
1650 result = iterator.next()
1651 }
1652 return { done: true, value: undefined }
1653 }
1654 }
1655 }
1656 })
1657)
1658
1659/**
1660 * Transforms all elements of the `Iterable` for as long as the specified function succeeds.
1661 *
1662 * **Example** (Filtering and transforming until failure)
1663 *
1664 * ```ts import.meta.vitest
1665 * import { Iterable, Result } from "effect"
1666 *
1667 * // Parse numbers until we hit an invalid one
1668 * const strings = ["1", "2", "3", "invalid", "4", "5"]
1669 * const numbers = Iterable.filterMapWhile(strings, (s) => {
1670 * const num = parseInt(s)
1671 * return isNaN(num) ? Result.failVoid : Result.succeed(num)
1672 * })
1673 * Array.from(numbers) // => [1, 2, 3]
1674 *
1675 * // Take elements while they meet a condition and transform them
1676 * const values = [2, 4, 6, 7, 8, 10]
1677 * const doubledEvens = Iterable.filterMapWhile(
1678 * values,
1679 * (n) => n % 2 === 0 ? Result.succeed(n * 2) : Result.failVoid
1680 * )
1681 * Array.from(doubledEvens) // => [4, 8, 12]
1682 *
1683 * // Process with index until condition fails
1684 * const letters = ["a", "b", "c", "d", "e"]
1685 * const indexedUntilC = Iterable.filterMapWhile(
1686 * letters,
1687 * (letter, i) => letter !== "c" ? Result.succeed(`${i}: ${letter}`) : Result.failVoid
1688 * )
1689 * Array.from(indexedUntilC) // => ["0: a", "1: b"]
1690 * ```
1691 *
1692 * @category filtering
1693 * @since 2.0.0
1694 */
1695export const filterMapWhile: {
1696 <A, B, X>(f: (input: A, i: number) => Result<B, X>): (self: Iterable<A>) => Iterable<B>
1697 <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>): Iterable<B>
1698} = dual(2, <A, B, X>(self: Iterable<A>, f: (input: A, i: number) => Result<B, X>) => ({
1699 [Symbol.iterator]() {
1700 const iterator = self[Symbol.iterator]()
1701 let i = 0
1702 return {
1703 next() {
1704 const result = iterator.next()
1705 if (result.done) {
1706 return { done: true, value: undefined }
1707 }
1708 const next = f(result.value, i++)
1709 if (R.isSuccess(next)) {
1710 return { done: false, value: next.success }
1711 }
1712 return { done: true, value: undefined }
1713 }
1714 }
1715 }
1716}))
1717
1718/**
1719 * Retrieves the `Some` values from an `Iterable` of `Option`s.
1720 *
1721 * **Example** (Extracting Some values)
1722 *
1723 * ```ts import.meta.vitest
1724 * import { Iterable, Option } from "effect"
1725 *
1726 * Array.from(Iterable.getSomes([Option.some(1), Option.none(), Option.some(2)])) // => [1, 2]
1727 * ```
1728 *
1729 * @category filtering
1730 * @since 2.0.0
1731 */
1732export const getSomes = <A>(self: Iterable<Option<A>>): Iterable<A> => {
1733 return {
1734 [Symbol.iterator]() {
1735 const iterator = self[Symbol.iterator]()
1736 return {
1737 next() {
1738 let result = iterator.next()
1739 while (!result.done) {
1740 if (O.isSome(result.value)) {
1741 return { done: false, value: result.value.value }
1742 }
1743 result = iterator.next()
1744 }
1745 return { done: true, value: undefined }
1746 }
1747 }
1748 }
1749 }
1750}
1751
1752/**
1753 * Returns a lazy iterable containing the failure values from an iterable of
1754 * `Result`s, skipping successful results.
1755 *
1756 * **Example** (Extracting failures)
1757 *
1758 * ```ts import.meta.vitest
1759 * import { Iterable, Result } from "effect"
1760 *
1761 * Array.from(Iterable.getFailures([
1762 * Result.succeed(1),
1763 * Result.fail("err"),
1764 * Result.succeed(2)
1765 * ])) // => ["err"]
1766 * ```
1767 *
1768 * @category filtering
1769 * @since 4.0.0
1770 */
1771export const getFailures = <R0, L>(self: Iterable<Result<R0, L>>): Iterable<L> => {
1772 return {
1773 [Symbol.iterator]() {
1774 const iterator = self[Symbol.iterator]()
1775 return {
1776 next() {
1777 let result = iterator.next()
1778 while (!result.done) {
1779 if (R.isFailure(result.value)) {
1780 return { done: false, value: result.value.failure }
1781 }
1782 result = iterator.next()
1783 }
1784 return { done: true, value: undefined }
1785 }
1786 }
1787 }
1788 }
1789}
1790
1791/**
1792 * Returns a lazy iterable containing the success values from an iterable of
1793 * `Result`s, skipping failed results.
1794 *
1795 * **Example** (Extracting successes)
1796 *
1797 * ```ts import.meta.vitest
1798 * import { Iterable, Result } from "effect"
1799 *
1800 * Array.from(Iterable.getSuccesses([
1801 * Result.succeed(1),
1802 * Result.fail("err"),
1803 * Result.succeed(2)
1804 * ])) // => [1, 2]
1805 * ```
1806 *
1807 * @category filtering
1808 * @since 4.0.0
1809 */
1810export const getSuccesses = <R0, L>(self: Iterable<Result<R0, L>>): Iterable<R0> => {
1811 return {
1812 [Symbol.iterator]() {
1813 const iterator = self[Symbol.iterator]()
1814 return {
1815 next() {
1816 let result = iterator.next()
1817 while (!result.done) {
1818 if (R.isSuccess(result.value)) {
1819 return { done: false, value: result.value.success }
1820 }
1821 result = iterator.next()
1822 }
1823 return { done: true, value: undefined }
1824 }
1825 }
1826 }
1827 }
1828}
1829
1830/**
1831 * Filters an iterable to only include elements that match a predicate.
1832 *
1833 * **Details**
1834 *
1835 * This function creates a new iterable containing only the elements for which
1836 * the predicate function returns true. Like map, this operation is lazy and
1837 * elements are only tested when the iterable is consumed.
1838 *
1839 * **Example** (Filtering elements)
1840 *
1841 * ```ts import.meta.vitest
1842 * import { Iterable } from "effect"
1843 *
1844 * // Filter even numbers
1845 * const numbers = [1, 2, 3, 4, 5, 6]
1846 * const evens = Iterable.filter(numbers, (x) => x % 2 === 0)
1847 * Array.from(evens) // => [2, 4, 6]
1848 *
1849 * // Filter with index
1850 * const items = ["a", "b", "c", "d"]
1851 * const oddPositions = Iterable.filter(items, (_, i) => i % 2 === 1)
1852 * Array.from(oddPositions) // => ["b", "d"]
1853 *
1854 * // Type refinement
1855 * const mixed: Array<string | number> = ["hello", 42, "world", 100]
1856 * const onlyStrings = Iterable.filter(
1857 * mixed,
1858 * (x): x is string => typeof x === "string"
1859 * )
1860 * Array.from(onlyStrings) // => ["hello", "world"]
1861 *
1862 * // Combine with map
1863 * const processed = Iterable.map(
1864 * Iterable.filter([1, 2, 3, 4, 5], (x) => x > 2),
1865 * (x) => x * 10
1866 * )
1867 * Array.from(processed) // => [30, 40, 50]
1868 * ```
1869 *
1870 * @category filtering
1871 * @since 2.0.0
1872 */
1873export const filter: {
1874 <A, B extends A>(refinement: (a: NoInfer<A>, i: number) => a is B): (self: Iterable<A>) => Iterable<B>
1875 <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => Iterable<A>
1876 <A, B extends A>(self: Iterable<A>, refinement: (a: A, i: number) => a is B): Iterable<B>
1877 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A>
1878} = dual(
1879 2,
1880 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): Iterable<A> => ({
1881 [Symbol.iterator]() {
1882 const iterator = self[Symbol.iterator]()
1883 let i = 0
1884 return {
1885 next() {
1886 let result = iterator.next()
1887 while (!result.done) {
1888 if (predicate(result.value, i++)) {
1889 return { done: false, value: result.value }
1890 }
1891 result = iterator.next()
1892 }
1893 return { done: true, value: undefined }
1894 }
1895 }
1896 }
1897 })
1898)
1899
1900/**
1901 * Transforms elements using a function that may return null or undefined, filtering out the null/undefined results.
1902 *
1903 * **When to use**
1904 *
1905 * Use when working with APIs or functions that return nullable values,
1906 * providing a clean way to filter out null or undefined while transforming.
1907 *
1908 * **Example** (Flat mapping nullable results)
1909 *
1910 * ```ts import.meta.vitest
1911 * import { Iterable } from "effect"
1912 *
1913 * // Extract valid elements from nullable function results
1914 * const data = ["1", "2", "invalid", "4"]
1915 * const parsed = Iterable.flatMapNullishOr(data, (s) => {
1916 * const num = parseInt(s)
1917 * return isNaN(num) ? null : num * 2
1918 * })
1919 * Array.from(parsed) // => [2, 4, 8]
1920 *
1921 * // Safe property access
1922 * const objects = [
1923 * { nested: { value: 10 } },
1924 * { nested: null },
1925 * { nested: { value: 20 } },
1926 * {}
1927 * ]
1928 * const values = Iterable.flatMapNullishOr(objects, (obj) => obj.nested?.value)
1929 * Array.from(values) // => [10, 20]
1930 *
1931 * // Working with Map.get (returns undefined for missing keys)
1932 * const map = new Map([
1933 * ["a", 1],
1934 * ["b", 2],
1935 * ["c", 3]
1936 * ])
1937 * const keys = ["a", "x", "b", "y", "c"]
1938 * const foundValues = Iterable.flatMapNullishOr(keys, (key) => map.get(key))
1939 * Array.from(foundValues) // => [1, 2, 3]
1940 * ```
1941 *
1942 * @category sequencing
1943 * @since 4.0.0
1944 */
1945export const flatMapNullishOr: {
1946 <A, B>(f: (a: A) => B): (self: Iterable<A>) => Iterable<NonNullable<B>>
1947 <A, B>(self: Iterable<A>, f: (a: A) => B): Iterable<NonNullable<B>>
1948} = dual(
1949 2,
1950 <A, B>(self: Iterable<A>, f: (a: A) => B): Iterable<NonNullable<B>> =>
1951 filterMap(self, (a) => {
1952 const b = f(a)
1953 return b == null ? R.failVoid : R.succeed(b)
1954 })
1955)
1956
1957/**
1958 * Checks whether a predicate holds true for some `Iterable` element.
1959 *
1960 * **Example** (Checking whether some element matches)
1961 *
1962 * ```ts import.meta.vitest
1963 * import { Iterable } from "effect"
1964 *
1965 * const numbers = [1, 3, 5, 7, 8]
1966 * const hasEven = Iterable.some(numbers, (x) => x % 2 === 0)
1967 * hasEven // => true
1968 *
1969 * const allOdd = [1, 3, 5, 7]
1970 * const hasEvenInAllOdd = Iterable.some(allOdd, (x) => x % 2 === 0)
1971 * hasEvenInAllOdd // => false
1972 *
1973 * // With index
1974 * const letters = ["a", "b", "c"]
1975 * const hasElementAtIndex2 = Iterable.some(letters, (_, i) => i === 2)
1976 * hasElementAtIndex2 // => true
1977 *
1978 * // Early termination - stops at first match
1979 * const infiniteOdds = Iterable.filter(Iterable.range(1), (x) => x % 2 === 1)
1980 * const hasEvenInInfiniteOdds = Iterable.some(
1981 * Iterable.take(infiniteOdds, 1000),
1982 * (x) => x % 2 === 0
1983 * )
1984 * hasEvenInInfiniteOdds // => false
1985 *
1986 * // Type guard usage
1987 * const mixed: Array<string | number> = [1, 2, "hello"]
1988 * const hasString = Iterable.some(
1989 * mixed,
1990 * (x): x is string => typeof x === "string"
1991 * )
1992 * hasString // => true
1993 * ```
1994 *
1995 * @category predicates
1996 * @since 2.0.0
1997 */
1998export const some: {
1999 <A>(predicate: (a: A, i: number) => boolean): (self: Iterable<A>) => boolean
2000 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): boolean
2001} = dual(
2002 2,
2003 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): boolean => {
2004 let i = 0
2005 for (const a of self) {
2006 if (predicate(a, i++)) {
2007 return true
2008 }
2009 }
2010 return false
2011 }
2012)
2013
2014/**
2015 * Generates an iterable by repeatedly applying a function that produces the
2016 * next element and state.
2017 *
2018 * **Details**
2019 *
2020 * This is useful for creating iterables from a generating function that
2021 * maintains state. The function should return `Option.some([value, nextState])`
2022 * to continue or `Option.none()` to stop.
2023 *
2024 * **Example** (Unfolding state into values)
2025 *
2026 * ```ts import.meta.vitest
2027 * import { Iterable, Option } from "effect"
2028 *
2029 * // Generate Fibonacci sequence
2030 * const fibonacci = Iterable.unfold([0, 1], ([a, b]) => Option.some([a, [b, a + b]]))
2031 * const first10Fib = Iterable.take(fibonacci, 10)
2032 * Array.from(first10Fib) // => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
2033 *
2034 * // Generate powers of 2 up to a limit
2035 * const powersOf2 = Iterable.unfold(1, (n) => n <= 1000 ? Option.some([n, n * 2]) : Option.none())
2036 * Array.from(powersOf2) // => [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
2037 *
2038 * // Generate countdown
2039 * const countdown = Iterable.unfold(5, (n) => n > 0 ? Option.some([n, n - 1]) : Option.none())
2040 * Array.from(countdown) // => [5, 4, 3, 2, 1]
2041 *
2042 * // Generate collatz sequence
2043 * const collatz = Iterable.unfold(7, (n) => {
2044 * if (n === 1) return Option.none()
2045 * const next = n % 2 === 0 ? n / 2 : n * 3 + 1
2046 * return Option.some([n, next])
2047 * })
2048 * Array.from(collatz) // => [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2]
2049 * ```
2050 *
2051 * @category constructors
2052 * @since 2.0.0
2053 */
2054export const unfold = <B, A>(b: B, f: (b: B) => Option<readonly [A, B]>): Iterable<A> => ({
2055 [Symbol.iterator]() {
2056 let next = b
2057 return {
2058 next() {
2059 const ab = f(next)
2060 if (O.isNone(ab)) {
2061 return { done: true, value: undefined }
2062 }
2063 const [a, b] = ab.value
2064 next = b
2065 return { done: false, value: a }
2066 }
2067 }
2068 }
2069})
2070
2071/**
2072 * Iterates over the `Iterable`, applying `f` to each element.
2073 *
2074 * **Example** (Iterating with side effects)
2075 *
2076 * ```ts import.meta.vitest
2077 * import { Iterable } from "effect"
2078 *
2079 * // Collect each visited element
2080 * const numbers = [1, 2, 3, 4, 5]
2081 * const visited: Array<number> = []
2082 * Iterable.forEach(numbers, (n) => visited.push(n))
2083 * visited // => [1, 2, 3, 4, 5]
2084 *
2085 * // Use index in the callback
2086 * const letters = ["a", "b", "c"]
2087 * const indexed: Array<string> = []
2088 * Iterable.forEach(letters, (letter, i) => {
2089 * indexed.push(`${i}: ${letter}`)
2090 * })
2091 * indexed // => ["0: a", "1: b", "2: c"]
2092 *
2093 * // Side effects with any iterable
2094 * const results: Array<number> = []
2095 * Iterable.forEach(Iterable.range(1, 5), (n) => {
2096 * results.push(n * n)
2097 * })
2098 * results // => [1, 4, 9, 16, 25]
2099 *
2100 * // Process in chunks
2101 * const data = Iterable.chunksOf([1, 2, 3, 4, 5, 6], 2)
2102 * const processed: Array<Array<number>> = []
2103 * Iterable.forEach(data, (chunk) => {
2104 * processed.push(Array.from(chunk))
2105 * })
2106 * processed // => [[1, 2], [3, 4], [5, 6]]
2107 * ```
2108 *
2109 * @category traversing
2110 * @since 2.0.0
2111 */
2112export const forEach: {
2113 <A>(f: (a: A, i: number) => void): (self: Iterable<A>) => void
2114 <A>(self: Iterable<A>, f: (a: A, i: number) => void): void
2115} = dual(2, <A>(self: Iterable<A>, f: (a: A, i: number) => void): void => {
2116 let i = 0
2117 for (const a of self) {
2118 f(a, i++)
2119 }
2120})
2121
2122/**
2123 * Reduces an iterable to a single value by applying a function to each element and accumulating the result.
2124 *
2125 * **Details**
2126 *
2127 * This function applies a reducing function against an accumulator and each element
2128 * of the iterable (from left to right) to reduce it to a single value.
2129 *
2130 * **Example** (Reducing an iterable)
2131 *
2132 * ```ts import.meta.vitest
2133 * import { Iterable } from "effect"
2134 *
2135 * // Sum all numbers
2136 * const numbers = [1, 2, 3, 4, 5]
2137 * const sum = Iterable.reduce(numbers, 0, (acc, n) => acc + n)
2138 * sum // => 15
2139 *
2140 * // Find maximum value
2141 * const values = [3, 1, 4, 1, 5, 9, 2]
2142 * Iterable.reduce(values, -Infinity, (max, value) => Math.max(max, value)) // => 9
2143 *
2144 * // Build an object from key-value pairs
2145 * const pairs = [["a", 1], ["b", 2], ["c", 3]] as const
2146 * const obj = Iterable.reduce(
2147 * pairs,
2148 * {} as Record<string, number>,
2149 * (acc, [key, value]) => {
2150 * acc[key] = value
2151 * return acc
2152 * }
2153 * )
2154 * obj // => { a: 1, b: 2, c: 3 }
2155 *
2156 * // Use index in the reducer
2157 * const letters = ["a", "b", "c"]
2158 * const indexed = Iterable.reduce(
2159 * letters,
2160 * [] as Array<string>,
2161 * (acc, letter, i) => {
2162 * acc.push(`${i}: ${letter}`)
2163 * return acc
2164 * }
2165 * )
2166 * indexed // => ["0: a", "1: b", "2: c"]
2167 * ```
2168 *
2169 * @category folding
2170 * @since 2.0.0
2171 */
2172export const reduce: {
2173 <B, A>(b: B, f: (b: B, a: A, i: number) => B): (self: Iterable<A>) => B
2174 <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A, i: number) => B): B
2175} = dual(3, <A, B>(self: Iterable<A>, b: B, f: (b: B, a: A, i: number) => B): B => {
2176 if (Array.isArray(self)) {
2177 return self.reduce(f, b)
2178 }
2179 let i = 0
2180 let result = b
2181 for (const n of self) {
2182 result = f(result, n, i++)
2183 }
2184 return result
2185})
2186
2187/**
2188 * Deduplicates adjacent elements that are identical using the provided `isEquivalent` function.
2189 *
2190 * **Example** (Deduplicating adjacent elements with custom equivalence)
2191 *
2192 * ```ts import.meta.vitest
2193 * import { Iterable } from "effect"
2194 *
2195 * // Remove adjacent duplicates with custom equality
2196 * const numbers = [1, 1, 2, 2, 3, 1, 1]
2197 * const dedupedNumbers = Iterable.dedupeAdjacentWith(numbers, (a, b) => a === b)
2198 * Array.from(dedupedNumbers) // => [1, 2, 3, 1]
2199 *
2200 * // Case-insensitive deduplication
2201 * const words = ["Hello", "HELLO", "world", "World", "test"]
2202 * const caseInsensitive = (a: string, b: string) =>
2203 * a.toLowerCase() === b.toLowerCase()
2204 * const dedupedWords = Iterable.dedupeAdjacentWith(words, caseInsensitive)
2205 * Array.from(dedupedWords) // => ["Hello", "world", "test"]
2206 *
2207 * // Deduplication by object property
2208 * const users = [
2209 * { id: 1, name: "Alice" },
2210 * { id: 1, name: "Alice Updated" }, // different name, same id
2211 * { id: 2, name: "Bob" },
2212 * { id: 2, name: "Bob" },
2213 * { id: 3, name: "Charlie" }
2214 * ]
2215 * const byId = (a: typeof users[0], b: typeof users[0]) => a.id === b.id
2216 * const dedupedUsers = Iterable.dedupeAdjacentWith(users, byId)
2217 * Array.from(dedupedUsers, (user) => user.id) // => [1, 2, 3]
2218 *
2219 * // Approximate numeric equality
2220 * const floats = [1.0, 1.01, 1.02, 2.0, 2.01, 3.0]
2221 * const approxEqual = (a: number, b: number) => Math.abs(a - b) < 0.1
2222 * const dedupedFloats = Iterable.dedupeAdjacentWith(floats, approxEqual)
2223 * Array.from(dedupedFloats) // => [1, 2, 3]
2224 * ```
2225 *
2226 * @category filtering
2227 * @since 2.0.0
2228 */
2229export const dedupeAdjacentWith: {
2230 <A>(isEquivalent: (self: A, that: A) => boolean): (self: Iterable<A>) => Iterable<A>
2231 <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<A>
2232} = dual(2, <A>(self: Iterable<A>, isEquivalent: (self: A, that: A) => boolean): Iterable<A> => ({
2233 [Symbol.iterator]() {
2234 const iterator = self[Symbol.iterator]()
2235 let first = true
2236 let last: A
2237 function next(): IteratorResult<A> {
2238 const result = iterator.next()
2239 if (result.done) {
2240 return { done: true, value: undefined }
2241 }
2242 if (first) {
2243 first = false
2244 last = result.value
2245 return result
2246 }
2247 const current = result.value
2248 if (isEquivalent(last, current)) {
2249 return next()
2250 }
2251 last = current
2252 return result
2253 }
2254 return { next }
2255 }
2256}))
2257
2258/**
2259 * Deduplicates adjacent elements that are identical.
2260 *
2261 * **Example** (Deduplicating adjacent elements)
2262 *
2263 * ```ts import.meta.vitest
2264 * import { Iterable } from "effect"
2265 *
2266 * // Remove adjacent duplicate numbers
2267 * const numbers = [1, 1, 2, 2, 2, 3, 1, 1]
2268 * const deduped = Iterable.dedupeAdjacent(numbers)
2269 * Array.from(deduped) // => [1, 2, 3, 1]
2270 *
2271 * // Remove adjacent duplicate characters
2272 * const letters = "aabbccaa"
2273 * const dedupedLetters = Iterable.dedupeAdjacent(letters)
2274 * Array.from(dedupedLetters) // => ["a", "b", "c", "a"]
2275 *
2276 * // Works with objects using deep equality
2277 * const objects = [
2278 * { type: "A" },
2279 * { type: "A" },
2280 * { type: "B" },
2281 * { type: "B" },
2282 * { type: "A" }
2283 * ]
2284 * const dedupedObjects = Iterable.dedupeAdjacent(objects)
2285 * Array.from(dedupedObjects, (object) => object.type) // => ["A", "B", "A"]
2286 *
2287 * // Clean up streaming data
2288 * const sensorData = [100, 100, 100, 101, 101, 102, 102, 102, 100]
2289 * const cleanedData = Iterable.dedupeAdjacent(sensorData)
2290 * Array.from(cleanedData) // => [100, 101, 102, 100]
2291 * ```
2292 *
2293 * @category filtering
2294 * @since 2.0.0
2295 */
2296export const dedupeAdjacent: <A>(self: Iterable<A>) => Iterable<A> = dedupeAdjacentWith(Equal.asEquivalence())
2297
2298/**
2299 * Zips this Iterable crosswise with the specified Iterable using the specified combiner.
2300 *
2301 * **Example** (Combining cartesian products)
2302 *
2303 * ```ts import.meta.vitest
2304 * import { Iterable } from "effect"
2305 *
2306 * // Create coordinate pairs
2307 * const xs = [1, 2]
2308 * const ys = ["a", "b", "c"]
2309 * const coordinates = Iterable.cartesianWith(xs, ys, (x, y) => `(${x},${y})`)
2310 * Array.from(coordinates) // => ["(1,a)", "(1,b)", "(1,c)", "(2,a)", "(2,b)", "(2,c)"]
2311 *
2312 * // Generate all combinations of options
2313 * const sizes = ["S", "M", "L"]
2314 * const colors = ["red", "blue"]
2315 * const products = Iterable.cartesianWith(
2316 * sizes,
2317 * colors,
2318 * (size, color) => ({ size, color })
2319 * )
2320 * Array.from(products, ({ color, size }) => `${size}:${color}`) // => ["S:red", "S:blue", "M:red", "M:blue", "L:red", "L:blue"]
2321 *
2322 * // Mathematical operations on all pairs
2323 * const a = [1, 2, 3]
2324 * const b = [10, 20]
2325 * const mathProducts = Iterable.cartesianWith(a, b, (x, y) => x * y)
2326 * Array.from(mathProducts) // => [10, 20, 20, 40, 30, 60]
2327 *
2328 * // Create test data combinations
2329 * const userTypes = ["admin", "user"]
2330 * const features = ["read", "write", "delete"]
2331 * const testCases = Iterable.cartesianWith(
2332 * userTypes,
2333 * features,
2334 * (user, feature) => `${user}_can_${feature}`
2335 * )
2336 * Array.from(testCases) // => ["admin_can_read", "admin_can_write", "admin_can_delete", "user_can_read", "user_can_write", "user_can_delete"]
2337 * ```
2338 *
2339 * @category combining
2340 * @since 2.0.0
2341 */
2342export const cartesianWith: {
2343 <A, B, C>(that: Iterable<B>, f: (a: A, b: B) => C): (self: Iterable<A>) => Iterable<C>
2344 <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C>
2345} = dual(
2346 3,
2347 <A, B, C>(self: Iterable<A>, that: Iterable<B>, f: (a: A, b: B) => C): Iterable<C> => ({
2348 [Symbol.iterator]() {
2349 const cache: Array<B> = []
2350 let iterator: Iterator<B> | undefined
2351 let done = false
2352 const replay: Iterable<B> = {
2353 [Symbol.iterator]() {
2354 let index = 0
2355 return {
2356 next(): IteratorResult<B> {
2357 if (index < cache.length) {
2358 return { done: false, value: cache[index++] }
2359 }
2360 if (done) {
2361 return { done: true, value: undefined }
2362 }
2363 iterator ??= that[Symbol.iterator]()
2364 const result = iterator.next()
2365 if (result.done) {
2366 done = true
2367 return { done: true, value: undefined }
2368 }
2369 cache.push(result.value)
2370 index++
2371 return result
2372 }
2373 }
2374 }
2375 }
2376 return flatMap(self, (a) => map(replay, (b) => f(a, b)))[Symbol.iterator]()
2377 }
2378 })
2379)
2380
2381/**
2382 * Zips this Iterable crosswise with the specified Iterable.
2383 *
2384 * **Example** (Generating cartesian pairs)
2385 *
2386 * ```ts import.meta.vitest
2387 * import { Iterable } from "effect"
2388 *
2389 * // All pairs of numbers and letters
2390 * const numbers = [1, 2, 3]
2391 * const letters = ["a", "b"]
2392 * const pairs = Iterable.cartesian(numbers, letters)
2393 * Array.from(pairs) // => [[1, "a"], [1, "b"], [2, "a"], [2, "b"], [3, "a"], [3, "b"]]
2394 *
2395 * // Generate coordinate grid
2396 * const x = [0, 1, 2]
2397 * const y = [0, 1]
2398 * const grid = Iterable.cartesian(x, y)
2399 * Array.from(grid) // => [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]
2400 *
2401 * // All combinations for testing
2402 * const browsers = ["chrome", "firefox"]
2403 * const devices = ["desktop", "mobile", "tablet"]
2404 * const testMatrix = Iterable.cartesian(browsers, devices)
2405 * Array.from(testMatrix, ([browser, device]) => `${browser}:${device}`) // => ["chrome:desktop", "chrome:mobile", "chrome:tablet", "firefox:desktop", "firefox:mobile", "firefox:tablet"]
2406 *
2407 * // Empty iterable results in empty cartesian product
2408 * const empty = Iterable.empty<number>()
2409 * const withEmpty = Iterable.cartesian([1, 2], empty)
2410 * Array.from(withEmpty) // => []
2411 * ```
2412 *
2413 * @category combining
2414 * @since 2.0.0
2415 */
2416export const cartesian: {
2417 <B>(that: Iterable<B>): <A>(self: Iterable<A>) => Iterable<[A, B]>
2418 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]>
2419} = dual(
2420 2,
2421 <A, B>(self: Iterable<A>, that: Iterable<B>): Iterable<[A, B]> => cartesianWith(self, that, (a, b) => [a, b])
2422)
2423
2424/**
2425 * Computes how many elements of the iterable pass the given predicate.
2426 *
2427 * **Example** (Counting matching elements)
2428 *
2429 * ```ts import.meta.vitest
2430 * import { Iterable } from "effect"
2431 *
2432 * Iterable.countBy([1, 2, 3, 4, 5], (n) => n % 2 === 0) // => 2
2433 * ```
2434 *
2435 * @category folding
2436 * @since 3.16.0
2437 */
2438export const countBy: {
2439 <A>(predicate: (a: NoInfer<A>, i: number) => boolean): (self: Iterable<A>) => number
2440 <A>(self: Iterable<A>, predicate: (a: A, i: number) => boolean): number
2441} = dual(
2442 2,
2443 <A>(
2444 self: Iterable<A>,
2445 f: (a: A, i: number) => boolean
2446 ): number => {
2447 let count = 0
2448 let i = 0
2449 for (const a of self) {
2450 if (f(a, i)) {
2451 count++
2452 }
2453 i++
2454 }
2455 return count
2456 }
2457)