iter

package
v2.2.72 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 14, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package iter provides functional programming utilities for Go 1.23+ iterators.

This package offers a comprehensive set of operations for working with lazy sequences using Go's native iter.Seq and iter.Seq2 types. It follows functional programming principles and provides monadic operations, transformations, and reductions.

The package supports:

  • Functor operations (Map, MapWithIndex, MapWithKey)
  • Monad operations (Chain, Flatten, Ap)
  • Filtering (Filter, FilterMap, FilterWithIndex, FilterWithKey)
  • Folding and reduction (Reduce, Fold, FoldMap)
  • Sequence construction (Of, From, MakeBy, Replicate)
  • Sequence combination (Zip, Prepend, Append)

All operations are lazy and only execute when the sequence is consumed via iteration.

Example usage:

// Create a sequence and transform it
seq := From(1, 2, 3, 4, 5)
doubled := Map(N.Mul(2))(seq)

// Filter and reduce
evens := Filter(func(x int) bool { return x%2 == 0 })(doubled)
sum := MonadReduce(evens, func(acc, x int) int { return acc + x }, 0)
// sum = 20 (2+4+6+8+10 from doubled evens)

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConcatMonoid added in v2.2.72

func ConcatMonoid[T any]() M.Monoid[Seq[T]]

ConcatMonoid returns a Monoid instance for Seq[T] that concatenates sequences sequentially. This is an alias for Monoid that makes the sequential concatenation behavior explicit.

A Monoid is an algebraic structure with an associative binary operation (concat) and an identity element (empty). For sequences, the concat operation appends one sequence after another in deterministic order, and the identity is an empty sequence.

This monoid is useful for functional composition patterns where you need to combine multiple sequences sequentially using monoid operations like Reduce, FoldMap, or when working with monadic operations that require a monoid instance.

Marble Diagram (Sequential Concatenation):

Seq1:   --1--2--3--|
Seq2:   --4--5--6--|
Concat: --1--2--3--4--5--6--|
        (deterministic order)

Marble Diagram (vs MergeMonoid):

ConcatMonoid:
  Seq1:   --1--2--3--|
  Seq2:              --4--5--6--|
  Result: --1--2--3--4--5--6--|

MergeMonoid:
  Seq1:   --1--2--3--|
  Seq2:   --4--5--6--|
  Result: --1-4-2-5-3-6--|
          (non-deterministic)

Type Parameters:

  • T: The type of elements in the sequences

Returns:

  • Monoid[Seq[T]]: A monoid instance with:
  • Concat: Appends sequences sequentially (deterministic order)
  • Empty: Returns an empty sequence

Properties:

  • Identity: concat(empty, x) = concat(x, empty) = x
  • Associativity: concat(concat(a, b), c) = concat(a, concat(b, c))
  • Deterministic: Elements always appear in the order of the input sequences

Comparison with MergeMonoid:

ConcatMonoid and MergeMonoid serve different purposes:

  • ConcatMonoid: Sequential concatenation

  • Order: Deterministic - elements from first sequence, then second, etc.

  • Concurrency: No concurrency - sequences are processed one after another

  • Performance: Lower overhead, no goroutines or channels

  • Use when: Order matters, no I/O operations, or simplicity is preferred

  • MergeMonoid: Concurrent merging

  • Order: Non-deterministic - elements interleaved based on timing

  • Concurrency: Spawns goroutines for each sequence

  • Performance: Better for I/O-bound operations, higher overhead for CPU-bound

  • Use when: Order doesn't matter, parallel I/O, or concurrent processing needed

Example Usage:

// Create a monoid for concatenating integer sequences
monoid := ConcatMonoid[int]()

// Use with Reduce to concatenate multiple sequences
sequences := []Seq[int]{
    From(1, 2, 3),
    From(4, 5, 6),
    From(7, 8, 9),
}
concatenated := MonadReduce(From(sequences...), monoid.Concat, monoid.Empty)
// yields: 1, 2, 3, 4, 5, 6, 7, 8, 9 (deterministic order)

Example with Empty Identity:

monoid := ConcatMonoid[int]()
seq := From(1, 2, 3)

// Concatenating with empty is identity
result1 := monoid.Concat(monoid.Empty, seq)  // same as seq
result2 := monoid.Concat(seq, monoid.Empty)  // same as seq

Example with FoldMap:

// Convert each number to a sequence and concatenate all results
monoid := ConcatMonoid[int]()
numbers := From(1, 2, 3)
result := MonadFoldMap(numbers, func(n int) Seq[int] {
    return From(n, n*10, n*100)
}, monoid)
// yields: 1, 10, 100, 2, 20, 200, 3, 30, 300 (deterministic order)

Example Comparing ConcatMonoid vs MergeMonoid:

seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)

// ConcatMonoid: Sequential, deterministic
concatMonoid := ConcatMonoid[int]()
concat := concatMonoid.Concat(seq1, seq2)
// Always yields: 1, 2, 3, 4, 5, 6

// MergeMonoid: Concurrent, non-deterministic
mergeMonoid := MergeMonoid[int](10)
merged := mergeMonoid.Concat(seq1, seq2)
// May yield: 1, 4, 2, 5, 3, 6 (order varies)

See Also:

  • Monoid: The base monoid function (alias)
  • MergeMonoid: Concurrent merging monoid
  • MonadChain: Sequential flattening of sequences
  • Empty: Creates an empty sequence
Example
monoid := ConcatMonoid[int]()
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)

result := monoid.Concat(seq1, seq2)
for v := range result {
	fmt.Println(v)
}
Output:
1
2
3
4
5
6
Example (Comparison)
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)

// ConcatMonoid: Sequential, deterministic
concatMonoid := ConcatMonoid[int]()
concat := concatMonoid.Concat(seq1, seq2)
fmt.Println("ConcatMonoid (always same order):")
for v := range concat {
	fmt.Println(v)
}

// MergeMonoid: Concurrent, non-deterministic
// Note: Output order may vary in actual runs
mergeMonoid := MergeMonoid[int](10)
merged := mergeMonoid.Concat(seq1, seq2)
fmt.Println("\nMergeMonoid (order may vary):")
collected := slices.Collect(merged)
// Sort for consistent test output
slices.Sort(collected)
for _, v := range collected {
	fmt.Println(v)
}
Output:
ConcatMonoid (always same order):
1
2
3
4
5
6

MergeMonoid (order may vary):
1
2
3
4
5
6
Example (Identity)
monoid := ConcatMonoid[int]()
seq := From(1, 2, 3)

// Left identity
result1 := monoid.Concat(monoid.Empty(), seq)
for v := range result1 {
	fmt.Println(v)
}

// Right identity
result2 := monoid.Concat(seq, monoid.Empty())
for v := range result2 {
	fmt.Println(v)
}
Output:
1
2
3
1
2
3
Example (Reduce)
monoid := ConcatMonoid[int]()
sequences := []Seq[int]{
	From(1, 2, 3),
	From(4, 5, 6),
	From(7, 8, 9),
}

result := MonadReduce(From(sequences...), monoid.Concat, monoid.Empty())
for v := range result {
	fmt.Println(v)
}
Output:
1
2
3
4
5
6
7
8
9

func Fold

func Fold[A any](m M.Monoid[A]) func(Seq[A]) A

Fold returns a function that folds a sequence using a monoid. This is the curried version of MonadFold.

Example:

import "github.com/IBM/fp-go/v2/number"
sumAll := Fold(number.MonoidSum[int]())
seq := From(1, 2, 3, 4, 5)
result := sumAll(seq)
// returns: 15

func FoldMap

func FoldMap[A, B any](m M.Monoid[B]) func(func(A) B) func(Seq[A]) B

FoldMap returns a function that maps and folds using a monoid. This is the curried version of MonadFoldMap.

Example:

import "github.com/IBM/fp-go/v2/string"
stringify := FoldMap(string.Monoid)(func(x int) string {
    return fmt.Sprintf("%d ", x)
})
seq := From(1, 2, 3)
result := stringify(seq)
// returns: "1 2 3 "
Example
seq := From("a", "b", "c")
fold := FoldMap[string](S.Monoid)(strings.ToUpper)
result := fold(seq)
fmt.Println(result)
Output:
ABC

func FoldMapWithIndex

func FoldMapWithIndex[A, B any](m M.Monoid[B]) func(func(int, A) B) func(Seq[A]) B

FoldMapWithIndex returns a function that maps with index and folds. This is the curried version of MonadFoldMapWithIndex.

Example:

import "github.com/IBM/fp-go/v2/string"
indexedStringify := FoldMapWithIndex(string.Monoid)(func(i int, s string) string {
    return fmt.Sprintf("%d:%s ", i, s)
})
seq := From("a", "b", "c")
result := indexedStringify(seq)
// returns: "0:a 1:b 2:c "

func FoldMapWithKey

func FoldMapWithKey[K, A, B any](m M.Monoid[B]) func(func(K, A) B) func(Seq2[K, A]) B

FoldMapWithKey returns a function that maps with key and folds. This is the curried version of MonadFoldMapWithKey.

func MapToArray

func MapToArray[A, B any](f func(A) B) func(Seq[A]) []B

MapToArray returns a function that maps elements and collects them into an array. This is the curried version of MonadMapToArray.

Type Parameters:

  • A: The type of elements in the input sequence
  • B: The type of elements in the output array

Parameters:

  • f: The mapping function to apply to each element

Returns:

  • A function that takes a sequence and returns a slice of mapped elements

Example:

double := MapToArray(N.Mul(2))
seq := From(1, 2, 3)
result := double(seq)
// returns: []int{2, 4, 6}

func MergeMonoid added in v2.2.72

func MergeMonoid[T any](bufSize int) M.Monoid[Seq[T]]

MergeMonoid creates a Monoid for merging sequences concurrently. The monoid combines two sequences by merging them concurrently with the specified buffer size, and uses an empty sequence as the identity element.

A Monoid is an algebraic structure with an associative binary operation (concat) and an identity element (empty). For sequences, the concat operation merges two sequences concurrently, and the identity is an empty sequence.

This is useful for functional composition patterns where you need to combine multiple sequences using monoid operations like Reduce, FoldMap, or when working with monadic operations that require a monoid instance.

Marble Diagram (Concurrent Merging):

Seq1:   --1--2--3--|
Seq2:   --4--5--6--|
Merge:  --1-4-2-5-3-6--|
        (non-deterministic order)

Marble Diagram (vs ConcatMonoid):

MergeMonoid (concurrent):
  Seq1:   --1--2--3--|
  Seq2:   --4--5--6--|
  Result: --1-4-2-5-3-6--|
          (elements interleaved)

ConcatMonoid (sequential):
  Seq1:   --1--2--3--|
  Seq2:              --4--5--6--|
  Result: --1--2--3--4--5--6--|
          (deterministic order)

Type Parameters:

  • T: The type of elements in the sequences

Parameters:

  • bufSize: The buffer size for the internal channel used during merging. This buffer size will be used for all merge operations performed by the monoid. Negative values are treated as 0 (unbuffered).

Returns:

  • Monoid[Seq[T]]: A monoid instance with:
  • Concat: Merges two sequences concurrently using Merge
  • Empty: Returns an empty sequence

Properties:

  • Identity: concat(empty, x) = concat(x, empty) = x
  • Associativity: concat(concat(a, b), c) = concat(a, concat(b, c)) Note: Due to concurrent execution, element order may vary between equivalent expressions

Example Usage:

// Create a monoid for merging integer sequences
monoid := MergeMonoid[int](10)

// Use with Reduce to merge multiple sequences
sequences := []Seq[int]{
    From(1, 2, 3),
    From(4, 5, 6),
    From(7, 8, 9),
}
merged := MonadReduce(From(sequences...), monoid.Concat, monoid.Empty)
// merged contains all elements from all sequences (order non-deterministic)

Example with Empty Identity:

monoid := MergeMonoid[int](5)
seq := From(1, 2, 3)

// Merging with empty is identity
result1 := monoid.Concat(monoid.Empty, seq)  // same as seq
result2 := monoid.Concat(seq, monoid.Empty)  // same as seq

Example with FoldMap:

// Convert each number to a sequence and merge all results
monoid := MergeMonoid[int](10)
numbers := From(1, 2, 3)
result := MonadFoldMap(numbers, func(n int) Seq[int] {
    return From(n, n*10, n*100)
}, monoid)
// result contains: 1, 10, 100, 2, 20, 200, 3, 30, 300 (order varies)

See Also:

  • Merge: The underlying merge function
  • MergeAll: Merges multiple sequences at once
  • Empty: Creates an empty sequence
Example

Example tests for MergeMonoid

monoid := MergeMonoid[int](10)
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)

result := monoid.Concat(seq1, seq2)
collected := toSlice(result)
slices.Sort(collected)
for _, v := range collected {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (FoldMap)
monoid := MergeMonoid[int](10)
numbers := From(1, 2, 3)

// Convert each number to a sequence and merge all
result := MonadFoldMap(numbers, func(n int) Seq[int] {
	return From(n, n*10)
}, monoid)

collected := toSlice(result)
slices.Sort(collected)
for _, v := range collected {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 10 20 30
Example (Identity)
monoid := MergeMonoid[int](10)
seq := From(1, 2, 3)

// Left identity
result1 := monoid.Concat(monoid.Empty(), seq)
fmt.Println("Left identity:", toSlice(result1))

// Right identity
result2 := monoid.Concat(seq, monoid.Empty())
fmt.Println("Right identity:", toSlice(result2))
Output:
Left identity: [1 2 3]
Right identity: [1 2 3]
Example (Reduce)
monoid := MergeMonoid[int](10)
sequences := From(
	From(1, 2),
	From(3, 4),
	From(5, 6),
)

result := MonadReduce(sequences, monoid.Concat, monoid.Empty())
collected := toSlice(result)
slices.Sort(collected)
for _, v := range collected {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6

func MonadFold

func MonadFold[A any](fa Seq[A], m M.Monoid[A]) A

MonadFold folds a sequence using a monoid's concat operation and empty value.

Example:

import "github.com/IBM/fp-go/v2/number"
seq := From(1, 2, 3, 4, 5)
sum := MonadFold(seq, number.MonoidSum[int]())
// returns: 15

func MonadFoldMap

func MonadFoldMap[A, B any](fa Seq[A], f func(A) B, m M.Monoid[B]) B

MonadFoldMap maps each element to a monoid value and combines them using the monoid.

Example:

import "github.com/IBM/fp-go/v2/string"
seq := From(1, 2, 3)
result := MonadFoldMap(seq, func(x int) string {
    return fmt.Sprintf("%d ", x)
}, string.Monoid)
// returns: "1 2 3 "

func MonadFoldMapWithIndex

func MonadFoldMapWithIndex[A, B any](fa Seq[A], f func(int, A) B, m M.Monoid[B]) B

MonadFoldMapWithIndex maps each element with its index to a monoid value and combines them.

Example:

import "github.com/IBM/fp-go/v2/string"
seq := From("a", "b", "c")
result := MonadFoldMapWithIndex(seq, func(i int, s string) string {
    return fmt.Sprintf("%d:%s ", i, s)
}, string.Monoid)
// returns: "0:a 1:b 2:c "

func MonadFoldMapWithKey

func MonadFoldMapWithKey[K, A, B any](fa Seq2[K, A], f func(K, A) B, m M.Monoid[B]) B

MonadFoldMapWithKey maps each key-value pair to a monoid value and combines them.

Example:

import "github.com/IBM/fp-go/v2/string"
seq := Of2("x", 10)
result := MonadFoldMapWithKey(seq, func(k string, v int) string {
    return fmt.Sprintf("%s:%d ", k, v)
}, string.Monoid)
// returns: "x:10 "

func MonadMapToArray

func MonadMapToArray[A, B any](fa Seq[A], f func(A) B) []B

MonadMapToArray maps each element in a sequence using a function and collects the results into an array. This is a convenience function that combines Map and collection into a single operation.

Type Parameters:

  • A: The type of elements in the input sequence
  • B: The type of elements in the output array

Parameters:

  • fa: The input sequence to map
  • f: The mapping function to apply to each element

Returns:

  • A slice containing all mapped elements

Example:

seq := From(1, 2, 3)
result := MonadMapToArray(seq, N.Mul(2))
// returns: []int{2, 4, 6}

func MonadReduce

func MonadReduce[A, B any](fa Seq[A], f func(B, A) B, initial B) B

MonadReduce reduces a sequence to a single value by applying a function to each element and an accumulator, starting with an initial value.

Marble Diagram:

Input:  --1--2--3--4--5--|
Reduce((acc, x) => acc + x, 0)
Output: ------------------15|
        (emits final result only)

RxJS Equivalent: [reduce] - https://rxjs.dev/api/operators/reduce

Example:

seq := From(1, 2, 3, 4, 5)
sum := MonadReduce(seq, func(acc, x int) int { return acc + x }, 0)
// returns: 15

func MonadReduceWithIndex

func MonadReduceWithIndex[A, B any](fa Seq[A], f func(int, B, A) B, initial B) B

MonadReduceWithIndex reduces a sequence using a function that also receives the element's index.

Example:

seq := From(10, 20, 30)
result := MonadReduceWithIndex(seq, func(i, acc, x int) int {
    return acc + (i * x)
}, 0)
// returns: 0*10 + 1*20 + 2*30 = 80

func MonadReduceWithKey

func MonadReduceWithKey[K, A, B any](fa Seq2[K, A], f func(K, B, A) B, initial B) B

MonadReduceWithKey reduces a key-value sequence using a function that receives the key.

Example:

seq := Of2("x", 10)
result := MonadReduceWithKey(seq, func(k string, acc int, v int) int {
    return acc + v
}, 0)
// returns: 10

func Monoid

func Monoid[T any]() M.Monoid[Seq[T]]

Monoid returns a Monoid instance for Seq[T]. The monoid's concat operation concatenates sequences sequentially, and the empty value is an empty sequence.

Marble Diagram:

Seq1:   --1--2--3--|
Seq2:   --4--5--6--|
Concat: --1--2--3--4--5--6--|

Example:

m := Monoid[int]()
seq1 := From(1, 2)
seq2 := From(3, 4)
result := m.Concat(seq1, seq2)
// yields: 1, 2, 3, 4
Example
m := Monoid[int]()
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
combined := m.Concat(seq1, seq2)
result := toSlice(combined)
fmt.Println(result)
Output:
[1 2 3 4 5 6]

func Reduce

func Reduce[A, B any](f func(B, A) B, initial B) func(Seq[A]) B

Reduce returns a function that reduces a sequence to a single value. This is the curried version of MonadReduce.

Example:

sum := Reduce(func(acc, x int) int { return acc + x }, 0)
seq := From(1, 2, 3, 4, 5)
result := sum(seq)
// returns: 15

func ReduceWithIndex

func ReduceWithIndex[A, B any](f func(int, B, A) B, initial B) func(Seq[A]) B

ReduceWithIndex returns a function that reduces with index. This is the curried version of MonadReduceWithIndex.

Example:

weightedSum := ReduceWithIndex(func(i, acc, x int) int {
    return acc + (i * x)
}, 0)
seq := From(10, 20, 30)
result := weightedSum(seq)
// returns: 80

func ReduceWithKey

func ReduceWithKey[K, A, B any](f func(K, B, A) B, initial B) func(Seq2[K, A]) B

ReduceWithKey returns a function that reduces key-value pairs. This is the curried version of MonadReduceWithKey.

Example:

sumValues := ReduceWithKey(func(k string, acc int, v int) int {
    return acc + v
}, 0)
seq := Of2("x", 10)
result := sumValues(seq)
// returns: 10

func Zip

func Zip[A, B any](fb Seq[B]) func(Seq[A]) Seq2[A, B]

Zip returns a function that zips a sequence with another sequence. This is the curried version of MonadZip.

Example:

seqA := From(1, 2, 3)
zipWithA := Zip(seqA)
seqB := From("a", "b", "c")
result := zipWithA(seqB)
// yields: (1, "a"), (2, "b"), (3, "c")

Types

type Endomorphism added in v2.0.3

type Endomorphism[A any] = endomorphism.Endomorphism[A]

Endomorphism is a function from a type to itself. It represents transformations that preserve the type.

Type Parameters:

  • A: The type being transformed

Example:

increment := func(x int) int { return x + 1 }
result := increment(5) // returns 6

func ApSL added in v2.0.3

func ApSL[S, T any](
	lens Lens[S, T],
	fa Seq[T],
) Endomorphism[Seq[S]]

ApSL applies a sequence of values to update a state field using a Lens.

This is a specialized version of ApS that works with Lenses (optics that focus on a field of a structure). It uses the Lens's Set function to update the field.

Type Parameters:

  • S: The state type
  • T: The type of the field being updated

Parameters:

  • lens: A Lens focusing on the field to update
  • fa: A sequence of values to set

Returns:

  • An Endomorphism on Seq[S] (transforms Seq[S] to Seq[S])

Example:

type State struct {
    Name string
    Age  int
}

ageLens := lens.Prop[State, int]("Age")
ages := From(25, 30, 35)

result := F.Pipe2(
    Do(State{Name: "Alice"}),
    ApSL(ageLens, ages),
)
// yields: State{Name: "Alice", Age: 25}, State{Name: "Alice", Age: 30}, State{Name: "Alice", Age: 35}

func BindL added in v2.0.3

func BindL[S, T any](
	lens Lens[S, T],
	f Kleisli[T, T],
) Endomorphism[Seq[S]]

BindL performs a monadic bind on a field of a structure using a Lens.

This function combines Lens-based field access with monadic binding. It extracts a field value using the Lens's Get, applies a Kleisli arrow to produce a sequence, and updates the field with each result using the Lens's Set.

Type Parameters:

  • S: The state type
  • T: The type of the field being accessed and updated

Parameters:

  • lens: A Lens focusing on the field to bind
  • f: A Kleisli arrow that takes the field value and produces a sequence

Returns:

  • An Endomorphism on Seq[S]

Example:

type State struct {
    Value int
}

valueLens := lens.Prop[State, int]("Value")

multiplyValues := func(v int) Seq[int] {
    return From(v, v*2, v*3)
}

result := F.Pipe2(
    Do(State{Value: 5}),
    BindL(valueLens, multiplyValues),
)
// yields: State{Value: 5}, State{Value: 10}, State{Value: 15}

func LetL added in v2.0.3

func LetL[S, T any](
	lens Lens[S, T],
	f Endomorphism[T],
) Endomorphism[Seq[S]]

LetL performs a pure computation on a field of a structure using a Lens.

This function extracts a field value using the Lens's Get, applies a pure function to compute a new value, and updates the field using the Lens's Set. It's useful for transforming fields without monadic effects.

Type Parameters:

  • S: The state type
  • T: The type of the field being transformed

Parameters:

  • lens: A Lens focusing on the field to transform
  • f: An Endomorphism that transforms the field value

Returns:

  • An Endomorphism on Seq[S]

Example:

type State struct {
    Count int
}

countLens := lens.Prop[State, int]("Count")

increment := func(n int) int { return n + 1 }

result := F.Pipe2(
    Do(State{Count: 5}),
    LetL(countLens, increment),
)
// yields: State{Count: 6}

func LetToL added in v2.0.3

func LetToL[S, T any](
	lens Lens[S, T],
	b T,
) Endomorphism[Seq[S]]

LetToL sets a field of a structure to a constant value using a Lens.

This is a specialized version of LetL that sets a field to a fixed value rather than computing it from the current value. It's useful for setting defaults or resetting fields.

Type Parameters:

  • S: The state type
  • T: The type of the field being set

Parameters:

  • lens: A Lens focusing on the field to set
  • b: The constant value to set

Returns:

  • An Endomorphism on Seq[S]

Example:

type State struct {
    Status string
}

statusLens := lens.Prop[State, string]("Status")

result := F.Pipe2(
    Do(State{Status: "pending"}),
    LetToL(statusLens, "active"),
)
// yields: State{Status: "active"}

type Iterator

type Iterator[T any] = stateless.Iterator[T]

Iterator is a stateless iterator type. It provides a functional interface for iterating over collections without maintaining internal state.

Type Parameters:

  • T: The type of elements produced by the iterator

type Kleisli

type Kleisli[A, B any] = func(A) Seq[B]

Kleisli represents a function that takes a value and returns a sequence. This is the monadic bind operation for sequences, also known as flatMap. It's used to chain operations that produce sequences.

Type Parameters:

  • A: The input type
  • B: The element type of the output sequence

Example:

duplicate := func(x int) Seq[int] { return From(x, x) }
result := Chain(duplicate)(From(1, 2, 3))
// yields: 1, 1, 2, 2, 3, 3

type Kleisli2

type Kleisli2[K, A, B any] = func(A) Seq2[K, B]

Kleisli2 represents a function that takes a value and returns a key-value sequence. This is the monadic bind operation for key-value sequences.

Type Parameters:

  • K: The key type in the output sequence
  • A: The input type
  • B: The value type in the output sequence

type Lens added in v2.0.3

type Lens[S, A any] = lens.Lens[S, A]

Lens is an optic that focuses on a field within a structure. It provides a functional way to get and set values in immutable data structures.

Type Parameters:

  • S: The structure type
  • A: The field type being focused on

type Operator

type Operator[A, B any] = Kleisli[Seq[A], B]

Operator represents a transformation from one sequence to another. It's a function that takes a Seq[A] and returns a Seq[B]. Operators are the building blocks for composing sequence transformations.

Type Parameters:

  • A: The element type of the input sequence
  • B: The element type of the output sequence

Example:

double := Map(func(x int) int { return x * 2 })
result := double(From(1, 2, 3))
// yields: 2, 4, 6

func Ap

func Ap[B, A any](fa Seq[A]) Operator[func(A) B, B]

Ap returns a function that applies functions to values. This is the curried version of MonadAp.

Example:

applyTo5 := Ap(From(5))
fns := From(N.Mul(2), N.Add(10))
result := applyTo5(fns)
// yields: 10, 15

func ApS added in v2.0.3

func ApS[S1, S2, T any](
	setter func(T) func(S1) S2,
	fa Seq[T],
) Operator[S1, S2]

ApS applies a sequence of values to update a state using applicative style.

This function combines applicative application with state updates. It takes a sequence of values and a setter function, and produces an operator that applies each value to update the state. This is useful for parallel composition of independent computations.

Type Parameters:

  • S1: The input state type
  • S2: The output state type
  • T: The type of values in the sequence

Parameters:

  • setter: A curried function that takes a value T and returns a function that updates S1 to S2
  • fa: A sequence of values to apply

Returns:

  • An Operator that transforms Seq[S1] to Seq[S2]

Example:

type State struct {
    X int
    Y int
}

setY := func(y int) func(State) State {
    return func(s State) State { s.Y = y; return s }
}

yValues := From(10, 20, 30)

result := F.Pipe2(
    Do(State{X: 5}),
    ApS(setY, yValues),
)
// yields: State{X: 5, Y: 10}, State{X: 5, Y: 20}, State{X: 5, Y: 30}

func Append

func Append[A any](tail A) Operator[A, A]

Append returns a function that adds an element to the end of a sequence.

Marble Diagram:

Input:  --1--2--3-----|
Append(4)
Output: --1--2--3--4--|

RxJS Equivalent: [endWith] - https://rxjs.dev/api/operators/endWith

Example:

seq := From(1, 2, 3)
result := Append(4)(seq)
// yields: 1, 2, 3, 4

func Bind added in v2.0.3

func Bind[S1, S2, T any](
	setter func(T) func(S1) S2,
	f Kleisli[S1, T],
) Operator[S1, S2]

Bind performs a monadic bind operation in do-notation style, chaining a computation that produces a sequence and updating the state with the result.

This function is the core of do-notation for sequences. It takes a Kleisli arrow (a function that returns a sequence) and a setter function that updates the state with the result. The setter is curried to allow partial application.

Type Parameters:

  • S1: The input state type
  • S2: The output state type
  • T: The type of value produced by the Kleisli arrow

Parameters:

  • setter: A curried function that takes a value T and returns a function that updates S1 to S2
  • f: A Kleisli arrow that takes S1 and produces a sequence of T

Returns:

  • An Operator that transforms Seq[S1] to Seq[S2]

Example:

type State struct {
    Value int
    Double int
}

setValue := func(v int) func(State) State {
    return func(s State) State { s.Value = v; return s }
}

getValues := func(s State) Seq[int] {
    return From(1, 2, 3)
}

result := F.Pipe2(
    Do(State{}),
    Bind(setValue, getValues),
)
// yields: State{Value: 1}, State{Value: 2}, State{Value: 3}
Example
setValue := func(v int) func(State) State {
	return func(s State) State {
		s.Value = v
		return s
	}
}

getValues := func(s State) Seq[int] {
	return From(1, 2, 3)
}

bindOp := Bind(setValue, getValues)
result := bindOp(Do(State{}))

for s := range result {
	fmt.Printf("Value: %d\n", s.Value)
}
Output:
Value: 1
Value: 2
Value: 3

func BindTo added in v2.0.3

func BindTo[S1, T any](
	setter func(T) S1,
) Operator[T, S1]

BindTo wraps a value into a structure using a setter function.

This is typically used at the beginning of a do-notation chain to convert a simple value into a structured state. It's the inverse of extracting a value from a structure.

Type Parameters:

  • S1: The structure type to create
  • T: The value type to wrap

Parameters:

  • setter: A function that takes a value T and creates a structure S1

Returns:

  • An Operator that transforms Seq[T] to Seq[S1]

Example:

type State struct {
    Value int
}

createState := func(v int) State {
    return State{Value: v}
}

result := F.Pipe2(
    From(1, 2, 3),
    BindTo(createState),
)
// yields: State{Value: 1}, State{Value: 2}, State{Value: 3}

func BindToP added in v2.0.3

func BindToP[S1, T any](
	setter Prism[S1, T],
) Operator[T, S1]

BindToP wraps a value into a structure using a Prism's ReverseGet function.

This is a specialized version of BindTo that works with Prisms (optics that focus on a case of a sum type). It uses the Prism's ReverseGet to construct the structure.

Type Parameters:

  • S1: The structure type to create
  • T: The value type to wrap

Parameters:

  • setter: A Prism that can construct S1 from T

Returns:

  • An Operator that transforms Seq[T] to Seq[S1]

Example:

// Assuming a Prism for wrapping int into a Result type
result := F.Pipe2(
    From(1, 2, 3),
    BindToP(successPrism),
)
// yields: Success(1), Success(2), Success(3)

func Chain

func Chain[A, B any](f func(A) Seq[B]) Operator[A, B]

Chain returns a function that chains (flatMaps) a sequence transformation. This is the curried version of MonadChain.

Example:

duplicate := Chain(func(x int) Seq[int] { return From(x, x) })
seq := From(1, 2, 3)
result := duplicate(seq)
// yields: 1, 1, 2, 2, 3, 3
Example
seq := From(1, 2)
result := F.Pipe2(
	seq,
	Chain(func(x int) Seq[int] {
		return From(x, x*10)
	}),
	toSlice[int],
)
fmt.Println(result)
Output:
[1 10 2 20]

func ChainOptionK added in v2.2.16

func ChainOptionK[A, B any](f option.Kleisli[A, B]) Operator[A, B]

ChainOptionK returns an operator that chains a function returning an Option into a sequence, filtering out None values and unwrapping Some values.

This is the curried version of MonadChainOptionK, useful for function composition and creating reusable transformations.

Marble Diagram:

Input:  --1--2--3--4--5-->
ChainOptionK(x => x > 2 ? Some(x) : None)
Output: --------3--4--5-->
        (filters out values <= 2)

RxJS Equivalent: [concatMap] combined with [filter] - https://rxjs.dev/api/operators/concatMap

Type parameters:

  • A: The element type of the input sequence
  • B: The element type of the output sequence (wrapped in Option by the function)

Parameters:

  • f: A function that takes an element and returns an Option[B]

Returns:

An Operator that transforms Seq[A] to Seq[B], filtering out None values

Example:

import (
    "strconv"
    F "github.com/IBM/fp-go/v2/function"
    O "github.com/IBM/fp-go/v2/option"
    I "github.com/IBM/fp-go/v2/iterator/iter"
)

// Create a reusable parser operator
parsePositive := I.ChainOptionK(func(x int) O.Option[int] {
    if x > 0 {
        return O.Some(x)
    }
    return O.None[int]()
})

result := F.Pipe1(
    I.From(-1, 2, -3, 4, 5),
    parsePositive,
)
// yields: 2, 4, 5 (negative numbers are filtered out)

func Compress added in v2.0.3

func Compress[U any](sel Seq[bool]) Operator[U, U]

Compress filters elements from a sequence based on a corresponding sequence of boolean selectors.

This function takes a sequence of boolean values and returns an operator that filters elements from the input sequence. An element is included in the output if and only if the corresponding boolean selector is true. The filtering stops when either sequence is exhausted.

The implementation works by:

  1. Zipping the input sequence with the selector sequence
  2. Converting the Seq2 to a sequence of Pairs
  3. Filtering to keep only pairs where the boolean (tail) is true
  4. Extracting the original values (head) from the filtered pairs

Marble Diagram:

Data:      --1--2--3--4--5-->
Selectors: --T--F--T--F--T-->
Compress
Output:    --1-----3-----5-->

RxJS Equivalent: Similar to combining [zip] with [filter] - https://rxjs.dev/api/operators/zip

Type Parameters:

  • U: The type of elements in the sequence to be filtered

Parameters:

  • sel: A sequence of boolean values used as selectors

Returns:

  • An Operator that filters elements based on the selector sequence

Example - Basic filtering:

data := iter.From(1, 2, 3, 4, 5)
selectors := iter.From(true, false, true, false, true)
filtered := iter.Compress(selectors)(data)
// yields: 1, 3, 5

Example - Shorter selector sequence:

data := iter.From("a", "b", "c", "d", "e")
selectors := iter.From(true, true, false)
filtered := iter.Compress(selectors)(data)
// yields: "a", "b" (stops when selectors are exhausted)

Example - All false selectors:

data := iter.From(1, 2, 3)
selectors := iter.From(false, false, false)
filtered := iter.Compress(selectors)(data)
// yields: nothing (empty sequence)

Example - All true selectors:

data := iter.From(10, 20, 30)
selectors := iter.From(true, true, true)
filtered := iter.Compress(selectors)(data)
// yields: 10, 20, 30 (all elements pass through)
Example

Example tests for documentation

data := From(1, 2, 3, 4, 5)
selectors := From(true, false, true, false, true)
filtered := Compress[int](selectors)(data)

for v := range filtered {
	fmt.Printf("%d ", v)
}
Output:
1 3 5
Example (AllFalse)
data := From(1, 2, 3)
selectors := From(false, false, false)
filtered := Compress[int](selectors)(data)

count := 0
for range filtered {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (AllTrue)
data := From(10, 20, 30)
selectors := From(true, true, true)
filtered := Compress[int](selectors)(data)

for v := range filtered {
	fmt.Printf("%d ", v)
}
Output:
10 20 30

func ConcatMap added in v2.2.72

func ConcatMap[A, B any](f func(A) Seq[B]) Operator[A, B]

ConcatMap is an alias for Chain that emphasizes sequential concatenation. It maps each element to a sequence and concatenates the results in order.

Unlike concurrent operations, ConcatMap preserves the order of elements: it fully processes each input element (yielding all elements from f(a)) before moving to the next input element.

Example:

seq := From(1, 2, 3)
result := ConcatMap(func(x int) Seq[int] {
    return From(x, x*10)
})(seq)
// yields: 1, 10, 2, 20, 3, 30 (order preserved)

func Filter

func Filter[A any](pred func(A) bool) Operator[A, A]

Filter returns a function that filters elements based on a predicate. This is the curried version of MonadFilter.

Example:

evens := Filter(func(x int) bool { return x%2 == 0 })
seq := From(1, 2, 3, 4, 5)
result := evens(seq)
// yields: 2, 4

func FilterMap

func FilterMap[A, B any](f option.Kleisli[A, B]) Operator[A, B]

FilterMap returns a function that filters and maps in one operation. This is the curried version of MonadFilterMap.

Example:

evenDoubled := FilterMap(func(x int) Option[int] {
    if x%2 == 0 {
        return option.Some(x * 2)
    }
    return option.None[int]()
})
seq := From(1, 2, 3, 4)
result := evenDoubled(seq)
// yields: 4, 8

func FilterMapWithIndex

func FilterMapWithIndex[A, B any](f func(int, A) Option[B]) Operator[A, B]

FilterMapWithIndex returns a function that filters and maps with index. This is the curried version of MonadFilterMapWithIndex.

Example:

evenIndexed := FilterMapWithIndex(func(i int, s string) Option[string] {
    if i%2 == 0 {
        return option.Some(s)
    }
    return option.None[string]()
})
seq := From("a", "b", "c", "d")
result := evenIndexed(seq)
// yields: "a", "c"

func FilterWithIndex

func FilterWithIndex[A any](pred func(int, A) bool) Operator[A, A]

FilterWithIndex returns a function that filters elements based on their index and value. This is the curried version of MonadFilterWithIndex.

Example:

evenIndices := FilterWithIndex(func(i int, s string) bool { return i%2 == 0 })
seq := From("a", "b", "c", "d")
result := evenIndices(seq)
// yields: "a", "c"

func Flap

func Flap[B, A any](a A) Operator[func(A) B, B]

Flap returns a function that applies a fixed value to functions. This is the curried version of MonadFlap.

func FlatMap added in v2.2.16

func FlatMap[A, B any](f func(A) Seq[B]) Operator[A, B]

func FlatMapOptionK added in v2.2.16

func FlatMapOptionK[A, B any](f option.Kleisli[A, B]) Operator[A, B]

FlatMapOptionK is an alias for ChainOptionK.

This provides a more familiar name for developers coming from other functional programming languages or libraries where "flatMap" is the standard terminology for the monadic bind operation.

Type parameters:

  • A: The element type of the input sequence
  • B: The element type of the output sequence (wrapped in Option by the function)

Parameters:

  • f: A function that takes an element and returns an Option[B]

Returns:

An Operator that transforms Seq[A] to Seq[B], filtering out None values

Example:

import (
    F "github.com/IBM/fp-go/v2/function"
    O "github.com/IBM/fp-go/v2/option"
    I "github.com/IBM/fp-go/v2/iterator/iter"
)

// Validate and transform data
validateAge := I.FlatMapOptionK(func(age int) O.Option[string] {
    if age >= 18 && age <= 120 {
        return O.Some(fmt.Sprintf("Valid age: %d", age))
    }
    return O.None[string]()
})

result := F.Pipe1(
    I.From(15, 25, 150, 30),
    validateAge,
)
// yields: "Valid age: 25", "Valid age: 30"

func Let added in v2.0.3

func Let[S1, S2, T any](
	key func(T) func(S1) S2,
	f func(S1) T,
) Operator[S1, S2]

Let performs a pure computation in do-notation style, updating the state with a computed value.

Unlike Bind, Let doesn't perform a monadic operation - it simply computes a value from the current state and updates the state with that value. This is useful for intermediate calculations that don't require sequencing.

Type Parameters:

  • S1: The input state type
  • S2: The output state type
  • T: The type of the computed value

Parameters:

  • key: A curried function that takes a value T and returns a function that updates S1 to S2
  • f: A function that computes T from S1

Returns:

  • An Operator that transforms Seq[S1] to Seq[S2]

Example:

type State struct {
    Value int
    Double int
}

setDouble := func(d int) func(State) State {
    return func(s State) State { s.Double = d; return s }
}

computeDouble := func(s State) int {
    return s.Value * 2
}

result := F.Pipe2(
    Do(State{Value: 5}),
    Let(setDouble, computeDouble),
)
// yields: State{Value: 5, Double: 10}
Example
setDouble := func(d int) func(State) State {
	return func(s State) State {
		s.Double = d
		return s
	}
}

computeDouble := func(s State) int {
	return s.Value * 2
}

letOp := Let(setDouble, computeDouble)
result := letOp(Do(State{Value: 5}))

for s := range result {
	fmt.Printf("Value: %d, Double: %d\n", s.Value, s.Double)
}
Output:
Value: 5, Double: 10

func LetTo added in v2.0.3

func LetTo[S1, S2, T any](
	key func(T) func(S1) S2,
	b T,
) Operator[S1, S2]

LetTo sets a field in the state to a constant value in do-notation style.

This is a specialized version of Let that doesn't compute the value from the state, but instead uses a fixed value. It's useful for setting constants or default values.

Type Parameters:

  • S1: The input state type
  • S2: The output state type
  • T: The type of the value to set

Parameters:

  • key: A curried function that takes a value T and returns a function that updates S1 to S2
  • b: The constant value to set

Returns:

  • An Operator that transforms Seq[S1] to Seq[S2]

Example:

type State struct {
    Name string
    Status string
}

setStatus := func(s string) func(State) State {
    return func(st State) State { st.Status = s; return st }
}

result := F.Pipe2(
    Do(State{Name: "Alice"}),
    LetTo(setStatus, "active"),
)
// yields: State{Name: "Alice", Status: "active"}
Example
setStatus := func(s string) func(State) State {
	return func(st State) State {
		st.Status = s
		return st
	}
}

letToOp := LetTo(setStatus, "active")
result := letToOp(Do(State{Value: 5}))

for s := range result {
	fmt.Printf("Value: %d, Status: %s\n", s.Value, s.Status)
}
Output:
Value: 5, Status: active

func Map

func Map[A, B any](f func(A) B) Operator[A, B]

Map returns a function that transforms each element in a sequence. This is the curried version of MonadMap.

Example:

double := Map(N.Mul(2))
seq := From(1, 2, 3)
result := double(seq)
// yields: 2, 4, 6

func MapWithIndex

func MapWithIndex[A, B any](f func(int, A) B) Operator[A, B]

MapWithIndex returns a function that transforms elements with their indices. This is the curried version of MonadMapWithIndex.

Example:

addIndex := MapWithIndex(func(i int, s string) string {
    return fmt.Sprintf("%d:%s", i, s)
})
seq := From("a", "b", "c")
result := addIndex(seq)
// yields: "0:a", "1:b", "2:c"

func MergeAll added in v2.2.72

func MergeAll[T any](bufSize int) Operator[Seq[T], T]

MergeAll creates an operator that flattens and merges a sequence of sequences concurrently. It takes a sequence of sequences (Seq[Seq[T]]) and produces a single flat sequence (Seq[T]) by spawning a goroutine for each inner sequence as it arrives, merging all their elements through a buffered channel. This enables dynamic concurrent processing where inner sequences can be produced and consumed concurrently.

Unlike Merge which takes a pre-defined slice of sequences, MergeAll processes sequences dynamically as they are produced by the outer sequence. This makes it ideal for scenarios where the number of sequences isn't known upfront or where sequences are generated on-the-fly.

Type Parameters:

  • T: The type of elements in the inner sequences

Parameters:

  • bufSize: The buffer size for the internal channel. Negative values are treated as 0 (unbuffered). A larger buffer allows more elements to be produced ahead of consumption, reducing contention between producers but using more memory.

Returns:

  • Operator[Seq[T], T]: A function that takes a sequence of sequences and returns a flat sequence

Behavior:

  • Spawns one goroutine for the outer sequence to iterate and spawn inner producers
  • Spawns one goroutine per inner sequence as it arrives from the outer sequence
  • Elements from different inner sequences are interleaved non-deterministically
  • Properly handles early termination: if the consumer stops iterating, all goroutines are cleaned up
  • The output channel is closed when both the outer sequence and all inner sequences are exhausted
  • No goroutines leak even with early termination
  • Thread-safe: multiple producers can safely send to the shared channel

Example Usage:

// Create a sequence of sequences dynamically
outer := From(
    From(1, 2, 3),
    From(4, 5, 6),
    From(7, 8, 9),
)
mergeAll := MergeAll[int](10)
merged := mergeAll(outer)

// Elements appear in non-deterministic order
for v := range merged {
    fmt.Println(v) // May print: 1, 4, 7, 2, 5, 8, 3, 6, 9 (order varies)
}

Example with Dynamic Generation:

// Generate sequences on-the-fly
outer := Map(func(n int) Seq[int] {
    return From(n, n*10, n*100)
})(From(1, 2, 3))
mergeAll := MergeAll[int](10)
merged := mergeAll(outer)

// Yields: 1, 10, 100, 2, 20, 200, 3, 30, 300 (order varies)
for v := range merged {
    fmt.Println(v)
}

Example with Early Termination:

outer := From(
    From(1, 2, 3, 4, 5),
    From(6, 7, 8, 9, 10),
    From(11, 12, 13, 14, 15),
)
mergeAll := MergeAll[int](5)
merged := mergeAll(outer)

// Stop after 5 elements - all goroutines will be properly cleaned up
count := 0
for v := range merged {
    fmt.Println(v)
    count++
    if count >= 5 {
        break
    }
}

Example with Chain:

// Use with Chain to flatten nested sequences
numbers := From(1, 2, 3)
result := Chain(func(n int) Seq[int] {
    return From(n, n*10)
})(numbers)
// This is equivalent to: MergeAll[int](0)(Map(...)(numbers))

See Also:

  • Merge: Merges a pre-defined slice of sequences
  • Chain: Sequentially flattens sequences (deterministic order)
  • Flatten: Flattens nested sequences sequentially
  • Async: Converts a single sequence to asynchronous
Example

Example tests for documentation

seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 10)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (EarlyTermination)
seq1 := From(1, 2, 3, 4, 5)
seq2 := From(6, 7, 8, 9, 10)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 5)

count := 0
for range merged {
	count++
	if count >= 3 {
		break
	}
}
fmt.Printf("Consumed %d elements\n", count)
Output:
Consumed 3 elements
Example (Operator)
// Create a sequence of sequences
outer := From(
	From(1, 2, 3),
	From(4, 5, 6),
)

// Flatten and merge concurrently
mergeAll := MergeAll[int](10)
merged := mergeAll(outer)

// Collect and sort for deterministic output
result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (Operator_dynamicGeneration)
// Generate sequences on-the-fly
outer := MonadMap(From(1, 2, 3), func(n int) Seq[int] {
	return From(n, n*10)
})

mergeAll := MergeAll[int](10)
merged := mergeAll(outer)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 10 20 30
Example (Operator_earlyTermination)
outer := From(
	From(1, 2, 3, 4, 5),
	From(6, 7, 8, 9, 10),
	From(11, 12, 13, 14, 15),
)

mergeAll := MergeAll[int](5)
merged := mergeAll(outer)

// Take only first 5 elements
count := 0
for range merged {
	count++
	if count >= 5 {
		break
	}
}

fmt.Printf("Consumed %d elements\n", count)
Output:
Consumed 5 elements
Example (Operator_threeSequences)
outer := From(
	From(1, 2),
	From(3, 4),
	From(5, 6),
)

mergeAll := MergeAll[int](10)
merged := mergeAll(outer)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (Operator_unbuffered)
outer := From(
	From(1, 2, 3),
	From(4, 5, 6),
)

// Unbuffered channel (bufSize 0)
mergeAll := MergeAll[int](0)
merged := mergeAll(outer)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (ThreeSequences)
seq1 := From(1, 2)
seq2 := From(3, 4)
seq3 := From(5, 6)
merged := MergeBuf([]Seq[int]{seq1, seq2, seq3}, 10)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6
Example (Unbuffered)
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 0)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6

func MergeMap added in v2.2.72

func MergeMap[A, B any](f func(A) Seq[B]) Operator[A, B]

MergeMap applies a function that returns a sequence to each element and merges the results concurrently using a default buffer size. This is a convenience wrapper around MergeMapBuf that uses a default buffer size of 8. It's the concurrent version of Chain (flatMap), where each mapped sequence is processed in parallel.

Type Parameters:

  • A: The type of elements in the input sequence
  • B: The type of elements in the output sequences

Parameters:

  • f: A function that transforms each input element into a sequence of output elements

Returns:

  • Operator[A, B]: A function that takes a sequence of A and returns a flat sequence of B

Behavior:

  • Uses a default buffer size of 8 for the internal channel
  • Applies f to each element in the input sequence to produce inner sequences
  • Spawns one goroutine per inner sequence to produce elements concurrently
  • Elements from different inner sequences are interleaved non-deterministically
  • Properly handles early termination with goroutine cleanup
  • Thread-safe: multiple producers can safely send to the shared channel

Comparison with Chain:

  • Chain: Sequential processing, deterministic order, no concurrency overhead
  • MergeMap: Concurrent processing, non-deterministic order, better for I/O-bound tasks

Example:

// Expand each number into a sequence concurrently
expand := MergeMap(func(n int) Seq[int] {
    return From(n, n*10, n*100)
})
seq := From(1, 2, 3)
result := expand(seq)

// Yields: 1, 10, 100, 2, 20, 200, 3, 30, 300 (order varies)
for v := range result {
    fmt.Println(v)
}

See Also:

  • MergeMapBuf: MergeMap with custom buffer size
  • Chain: Sequential version (deterministic order)
  • MergeAll: Merges pre-existing sequences concurrently
  • Map: Transforms elements without flattening
Example

ExampleMergeMap demonstrates basic MergeMap usage

// Expand each number into a sequence concurrently
expand := MergeMapBuf(func(n int) Seq[int] {
	return From(n, n*10)
}, 10)
seq := From(1, 2, 3)

// Collect results (order may vary due to concurrency)
result := slices.Collect(expand(seq))
fmt.Printf("Length: %d\n", len(result))
fmt.Printf("Contains 1: %v\n", slices.Contains(result, 1))
fmt.Printf("Contains 10: %v\n", slices.Contains(result, 10))
fmt.Printf("Contains 2: %v\n", slices.Contains(result, 2))
fmt.Printf("Contains 20: %v\n", slices.Contains(result, 20))
fmt.Printf("Contains 3: %v\n", slices.Contains(result, 3))
fmt.Printf("Contains 30: %v\n", slices.Contains(result, 30))
Output:
Length: 6
Contains 1: true
Contains 10: true
Contains 2: true
Contains 20: true
Contains 3: true
Contains 30: true
Example (EarlyTermination)

ExampleMergeMap_earlyTermination demonstrates early termination

expand := MergeMapBuf(func(n int) Seq[int] {
	return From(n, n*10, n*100)
}, 5)
seq := From(1, 2, 3, 4, 5)

// Stop after collecting 5 elements
count := 0
for range expand(seq) {
	count++
	if count >= 5 {
		break
	}
}

fmt.Printf("Collected %d elements\n", count)
Output:
Collected 5 elements
Example (Unbuffered)

ExampleMergeMap_unbuffered demonstrates unbuffered channel usage

// bufSize of 0 creates an unbuffered channel
expand := MergeMapBuf(func(n int) Seq[int] {
	return From(n, n*2)
}, 0)
seq := From(1, 2, 3)

result := slices.Collect(expand(seq))
fmt.Printf("Length: %d\n", len(result))
Output:
Length: 6

func MergeMapBuf added in v2.2.72

func MergeMapBuf[A, B any](f func(A) Seq[B], bufSize int) Operator[A, B]

MergeMapBuf applies a function that returns a sequence to each element and merges the results concurrently. This is the concurrent version of Chain (flatMap), where each mapped sequence is processed in parallel rather than sequentially. It combines Map and MergeAll into a single operation.

Unlike Chain which processes sequences sequentially (deterministic order), MergeMapBuf spawns a goroutine for each mapped sequence and merges their elements concurrently through a buffered channel. This makes it ideal for I/O-bound operations, parallel data processing, or when the order of results doesn't matter.

Type Parameters:

  • A: The type of elements in the input sequence
  • B: The type of elements in the output sequences

Parameters:

  • f: A function that transforms each input element into a sequence of output elements
  • bufSize: The buffer size for the internal channel. Negative values are treated as 0 (unbuffered). A larger buffer allows more elements to be produced ahead of consumption, reducing contention between producers but using more memory.

Returns:

  • Operator[A, B]: A function that takes a sequence of A and returns a flat sequence of B

Behavior:

  • Applies f to each element in the input sequence to produce inner sequences
  • Spawns one goroutine per inner sequence to produce elements concurrently
  • Elements from different inner sequences are interleaved non-deterministically
  • Properly handles early termination: if the consumer stops iterating, all goroutines are cleaned up
  • No goroutines leak even with early termination
  • Thread-safe: multiple producers can safely send to the shared channel

Comparison with Chain:

  • Chain: Sequential processing, deterministic order, no concurrency overhead
  • MergeMapBuf: Concurrent processing, non-deterministic order, better for I/O-bound tasks

Example Usage:

// Expand each number into a sequence concurrently
expand := MergeMapBuf(func(n int) Seq[int] {
    return From(n, n*10, n*100)
}, 10)
seq := From(1, 2, 3)
result := expand(seq)

// Yields: 1, 10, 100, 2, 20, 200, 3, 30, 300 (order varies)
for v := range result {
    fmt.Println(v)
}

Example with I/O Operations:

// Fetch data concurrently for each ID
fetchData := MergeMapBuf(func(id int) Seq[string] {
    // Simulate I/O operation
    data := fetchFromAPI(id)
    return From(data...)
}, 20)
ids := From(1, 2, 3, 4, 5)
results := fetchData(ids)

// All fetches happen concurrently
for data := range results {
    fmt.Println(data)
}

Example with Early Termination:

expand := MergeMapBuf(func(n int) Seq[int] {
    return From(n, n*10, n*100)
}, 5)
seq := From(1, 2, 3, 4, 5)
result := expand(seq)

// Stop after 5 elements - all goroutines will be properly cleaned up
count := 0
for v := range result {
    fmt.Println(v)
    count++
    if count >= 5 {
        break
    }
}

Example with Unbuffered Channel:

// bufSize of 0 creates an unbuffered channel
expand := MergeMapBuf(func(n int) Seq[int] {
    return From(n, n*2)
}, 0)
seq := From(1, 2, 3)
result := expand(seq)

// Producers and consumer are synchronized
for v := range result {
    fmt.Println(v)
}

See Also:

  • Chain: Sequential version (deterministic order)
  • MergeAll: Merges pre-existing sequences concurrently
  • Map: Transforms elements without flattening
  • Async: Converts a single sequence to asynchronous

func Prepend

func Prepend[A any](head A) Operator[A, A]

Prepend returns a function that adds an element to the beginning of a sequence.

Marble Diagram:

Input:  -----2--3--4-->
Prepend(1)
Output: --1--2--3--4-->

RxJS Equivalent: [startWith] - https://rxjs.dev/api/operators/startWith

Example:

seq := From(2, 3, 4)
result := Prepend(1)(seq)
// yields: 1, 2, 3, 4

func Scan added in v2.0.4

func Scan[FCT ~func(V, U) V, U, V any](f FCT, initial V) Operator[U, V]

Scan applies an accumulator function over a sequence, emitting each intermediate result.

This function is similar to Reduce, but instead of returning only the final accumulated value, it returns a sequence containing all intermediate accumulated values. Each element in the output sequence is the result of applying the accumulator function to the previous accumulated value and the current input element.

The operation is lazy - intermediate values are computed only as they are consumed.

Marble Diagram:

Input:  --1--2--3--4--5-->
Scan((acc, x) => acc + x, 0)
Output: --1--3--6--10-15->
        (running sum)

RxJS Equivalent: [scan] - https://rxjs.dev/api/operators/scan

Scan is useful for:

  • Computing running totals or cumulative sums
  • Tracking state changes over a sequence
  • Building up complex values incrementally
  • Generating sequences based on previous values

Type Parameters:

  • FCT: The accumulator function type, must be ~func(V, U) V
  • U: The type of elements in the input sequence
  • V: The type of the accumulated value and elements in the output sequence

Parameters:

  • f: The accumulator function that takes the current accumulated value and the next input element, returning the new accumulated value
  • initial: The initial accumulated value (not included in the output sequence)

Returns:

  • An Operator that transforms a Seq[U] into a Seq[V] containing all intermediate accumulated values

Example - Running sum:

seq := From(1, 2, 3, 4, 5)
runningSum := Scan(func(acc, x int) int { return acc + x }, 0)
result := runningSum(seq)
// yields: 1, 3, 6, 10, 15

Example - Running product:

seq := From(2, 3, 4)
runningProduct := Scan(func(acc, x int) int { return acc * x }, 1)
result := runningProduct(seq)
// yields: 2, 6, 24

Example - Building strings:

seq := From("a", "b", "c")
concat := Scan(func(acc, x string) string { return acc + x }, "")
result := concat(seq)
// yields: "a", "ab", "abc"

Example - Tracking maximum:

seq := From(3, 1, 4, 1, 5, 9, 2)
maxSoFar := Scan(func(acc, x int) int {
    if x > acc { return x }
    return acc
}, 0)
result := maxSoFar(seq)
// yields: 3, 3, 4, 4, 5, 9, 9

Example - Empty sequence:

seq := Empty[int]()
runningSum := Scan(func(acc, x int) int { return acc + x }, 0)
result := runningSum(seq)
// yields: nothing (empty sequence)

Example - Single element:

seq := From(42)
runningSum := Scan(func(acc, x int) int { return acc + x }, 10)
result := runningSum(seq)
// yields: 52
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
runningSum := Scan(func(acc, x int) int { return acc + x }, 0)
result := runningSum(seq)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 3 6 10 15
Example (Empty)
seq := Empty[int]()
runningSum := Scan(func(acc, x int) int { return acc + x }, 0)
result := runningSum(seq)

count := 0
for range result {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (RunningProduct)
seq := From(2, 3, 4)
runningProduct := Scan(func(acc, x int) int { return acc * x }, 1)
result := runningProduct(seq)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
2 6 24
Example (StringConcatenation)
seq := From("a", "b", "c")
concat := Scan(func(acc, x string) string { return acc + x }, "")
result := concat(seq)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
a ab abc
Example (TrackingMaximum)
seq := From(3, 1, 4, 1, 5, 9, 2)
maxSoFar := Scan(func(acc, x int) int {
	if x > acc {
		return x
	}
	return acc
}, 0)
result := maxSoFar(seq)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
3 3 4 4 5 9 9

func Skip added in v2.2.67

func Skip[U any](count int) Operator[U, U]

Skip returns an operator that skips the first n elements of a sequence.

This function creates a transformation that discards the first n elements from the source sequence and yields all remaining elements. If n is less than or equal to 0, all elements are yielded. If n is greater than or equal to the sequence length, an empty sequence is returned.

The operation is lazy and only consumes elements from the source sequence as needed. The first n elements are consumed and discarded, then subsequent elements are yielded.

Marble Diagram:

Input:  --1--2--3--4--5--6--7--8-->
Skip(3)
Output: -----------4--5--6--7--8-->

RxJS Equivalent: [skip] - https://rxjs.dev/api/operators/skip

Type Parameters:

  • U: The type of elements in the sequence

Parameters:

  • count: The number of elements to skip from the beginning of the sequence

Returns:

  • An Operator that transforms a Seq[U] by skipping the first count elements

Example - Skip first 3 elements:

seq := From(1, 2, 3, 4, 5)
result := Skip[int](3)(seq)
// yields: 4, 5

Example - Skip more than available:

seq := From(1, 2)
result := Skip[int](5)(seq)
// yields: nothing (empty sequence)

Example - Skip zero or negative:

seq := From(1, 2, 3)
result := Skip[int](0)(seq)
// yields: 1, 2, 3 (all elements)

Example - Chaining with other operations:

seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := F.Pipe2(
    seq,
    Skip[int](3),
    MonadFilter(seq, func(x int) bool { return x%2 == 0 }),
)
// yields: 4, 6, 8, 10 (skip first 3, then filter evens)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
skipped := Skip[int](3)(seq)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
4 5
Example (Chained)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := F.Pipe3(
	seq,
	Skip[int](3),
	Filter(func(x int) bool { return x%2 == 0 }),
	toSlice[int],
)

fmt.Println(result)
Output:
[4 6 8 10]
Example (MoreThanAvailable)
seq := From(1, 2, 3)
skipped := Skip[int](10)(seq)

count := 0
for range skipped {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (WithFilter)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
evens := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
skipped := Skip[int](2)(evens)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
6 8 10
Example (WithMap)
seq := From(1, 2, 3, 4, 5)
doubled := MonadMap(seq, N.Mul(2))
skipped := Skip[int](2)(doubled)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
6 8 10
Example (Zero)
seq := From(1, 2, 3, 4, 5)
skipped := Skip[int](0)(seq)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5

func SkipWhile added in v2.2.67

func SkipWhile[U any](p Predicate[U]) Operator[U, U]

SkipWhile returns an operator that skips elements from a sequence while a predicate is satisfied.

This function creates a transformation that discards elements from the source sequence as long as each element satisfies the provided predicate. Once an element fails the predicate test, that element and all subsequent elements are yielded, regardless of whether they satisfy the predicate.

The operation is lazy and only consumes elements from the source sequence as needed. Once the predicate returns false, all remaining elements are yielded without further predicate evaluation.

Marble Diagram:

Input:        --1--2--3--4--5--2--1-->
SkipWhile(x < 4)
Output:       -----------4--5--2--1-->
                         (starts at 4, continues with all)

RxJS Equivalent: [skipWhile] - https://rxjs.dev/api/operators/skipWhile

Type Parameters:

  • U: The type of elements in the sequence

Parameters:

  • p: A predicate function that tests each element. Returns true to skip, false to start yielding

Returns:

  • An Operator that transforms a Seq[U] by skipping elements while the predicate is satisfied

Example - Skip while less than threshold:

seq := From(1, 2, 3, 4, 5, 2, 1)
result := SkipWhile(func(x int) bool { return x < 4 })(seq)
// yields: 4, 5, 2, 1 (starts at 4, continues with all remaining)

Example - Skip while condition is met:

seq := From("a", "b", "c", "1", "d", "e")
isLetter := func(s string) bool { return s >= "a" && s <= "z" }
result := SkipWhile(isLetter)(seq)
// yields: "1", "d", "e" (starts at "1", continues with all remaining)

Example - Skip none when first element fails:

seq := From(5, 1, 2, 3)
result := SkipWhile(func(x int) bool { return x < 5 })(seq)
// yields: 5, 1, 2, 3 (first element fails predicate, all yielded)

Example - Skip all when predicate always true:

seq := From(2, 4, 6, 8)
result := SkipWhile(func(x int) bool { return x%2 == 0 })(seq)
// yields: nothing (all elements satisfy predicate)

Example - Chaining with other operations:

seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := F.Pipe2(
    seq,
    SkipWhile(func(x int) bool { return x < 5 }),
    MonadMap(seq, func(x int) int { return x * 2 }),
)
// yields: 10, 12, 14, 16, 18, 20 (skip until 5, then double remaining)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5, 2, 1)
skipped := SkipWhile(func(x int) bool { return x < 4 })(seq)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
4 5 2 1
Example (AllSatisfy)
seq := From(2, 4, 6, 8)
skipped := SkipWhile(func(x int) bool { return x%2 == 0 })(seq)

count := 0
for range skipped {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (FirstFails)
seq := From(5, 1, 2, 3)
skipped := SkipWhile(func(x int) bool { return x < 5 })(seq)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
5 1 2 3
Example (Strings)
seq := From("a", "b", "c", "1", "d", "e")
isLetter := func(s string) bool { return s >= "a" && s <= "z" }
skipped := SkipWhile(isLetter)(seq)

for v := range skipped {
	fmt.Printf("%s ", v)
}
Output:
1 d e
Example (WithMap)
seq := From(1, 2, 3, 4, 5)
doubled := MonadMap(seq, N.Mul(2))
skipped := SkipWhile(func(x int) bool { return x < 8 })(doubled)

for v := range skipped {
	fmt.Printf("%d ", v)
}
Output:
8 10

func Take added in v2.0.4

func Take[U any](n int) Operator[U, U]

Take returns an operator that limits the number of elements in a sequence to at most n elements.

This function creates a transformation that takes the first n elements from a sequence and discards the rest. If n is less than or equal to 0, it returns an empty sequence. If the input sequence has fewer than n elements, all elements are returned.

The operation is lazy and only consumes elements from the source sequence as needed. Once n elements have been yielded, iteration stops immediately without consuming the remaining elements from the source.

Marble Diagram:

Input:  --1--2--3--4--5--6--7--8-->
Take(3)
Output: --1--2--3|

RxJS Equivalent: [take] - https://rxjs.dev/api/operators/take

Type Parameters:

  • U: The type of elements in the sequence

Parameters:

  • n: The maximum number of elements to take from the sequence

Returns:

  • An Operator that transforms a Seq[U] by taking at most n elements

Example - Take first 3 elements:

seq := From(1, 2, 3, 4, 5)
result := Take[int](3)(seq)
// yields: 1, 2, 3

Example - Take more than available:

seq := From(1, 2)
result := Take[int](5)(seq)
// yields: 1, 2 (all available elements)

Example - Take zero or negative:

seq := From(1, 2, 3)
result := Take[int](0)(seq)
// yields: nothing (empty sequence)

Example - Chaining with other operations:

seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
evens := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
result := Take[int](3)(evens)
// yields: 2, 4, 6 (first 3 even numbers)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
taken := Take[int](3)(seq)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
1 2 3
Example (Chained)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := Take[int](5)(
	MonadFilter(seq, func(x int) bool { return x > 3 }),
)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
4 5 6 7 8
Example (MoreThanAvailable)
seq := From(1, 2, 3)
taken := Take[int](10)(seq)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
1 2 3
Example (WithFilter)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
evens := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
taken := Take[int](3)(evens)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
2 4 6
Example (WithMap)
seq := From(1, 2, 3, 4, 5)
doubled := MonadMap(seq, N.Mul(2))
taken := Take[int](3)(doubled)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
2 4 6
Example (Zero)
seq := From(1, 2, 3, 4, 5)
taken := Take[int](0)(seq)

count := 0
for range taken {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0

func TakeWhile added in v2.2.67

func TakeWhile[U any](p Predicate[U]) Operator[U, U]

TakeWhile returns an operator that emits elements from a sequence while a predicate is satisfied.

This function creates a transformation that yields elements from the source sequence as long as each element satisfies the provided predicate. Once an element fails the predicate test, the sequence terminates immediately, and no further elements are emitted, even if subsequent elements would satisfy the predicate.

The operation is lazy and only consumes elements from the source sequence as needed. Once the predicate returns false, iteration stops immediately without consuming the remaining elements from the source.

Marble Diagram:

Input:       --1--2--3--4--5--2--1-->
TakeWhile(x < 4)
Output:      --1--2--3|
                      (stops at 4)

RxJS Equivalent: [takeWhile] - https://rxjs.dev/api/operators/takeWhile

Type Parameters:

  • U: The type of elements in the sequence

Parameters:

  • p: A predicate function that tests each element. Returns true to continue, false to stop

Returns:

  • An Operator that transforms a Seq[U] by taking elements while the predicate is satisfied

Example - Take while less than threshold:

seq := From(1, 2, 3, 4, 5, 2, 1)
result := TakeWhile(func(x int) bool { return x < 4 })(seq)
// yields: 1, 2, 3 (stops at 4, doesn't continue to 2, 1)

Example - Take while condition is met:

seq := From("a", "b", "c", "1", "d", "e")
isLetter := func(s string) bool { return s >= "a" && s <= "z" }
result := TakeWhile(isLetter)(seq)
// yields: "a", "b", "c" (stops at "1")

Example - Take all when predicate always true:

seq := From(2, 4, 6, 8)
result := TakeWhile(func(x int) bool { return x%2 == 0 })(seq)
// yields: 2, 4, 6, 8 (all elements satisfy predicate)

Example - Take none when first element fails:

seq := From(5, 1, 2, 3)
result := TakeWhile(func(x int) bool { return x < 5 })(seq)
// yields: nothing (first element fails predicate)

Example - Chaining with other operations:

seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
result := F.Pipe2(
    seq,
    MonadMap(seq, func(x int) int { return x * 2 }),
    TakeWhile(func(x int) bool { return x < 10 }),
)
// yields: 2, 4, 6, 8 (stops when doubled value reaches 10)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5, 2, 1)
taken := TakeWhile(func(x int) bool { return x < 4 })(seq)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
1 2 3
Example (AllSatisfy)
seq := From(2, 4, 6, 8)
taken := TakeWhile(func(x int) bool { return x%2 == 0 })(seq)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
2 4 6 8
Example (FirstFails)
seq := From(5, 1, 2, 3)
taken := TakeWhile(func(x int) bool { return x < 5 })(seq)

count := 0
for range taken {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (Strings)
seq := From("a", "b", "c", "1", "d", "e")
isLetter := func(s string) bool { return s >= "a" && s <= "z" }
taken := TakeWhile(isLetter)(seq)

for v := range taken {
	fmt.Printf("%s ", v)
}
Output:
a b c
Example (WithMap)
seq := From(1, 2, 3, 4, 5)
doubled := MonadMap(seq, N.Mul(2))
taken := TakeWhile(func(x int) bool { return x < 8 })(doubled)

for v := range taken {
	fmt.Printf("%d ", v)
}
Output:
2 4 6

func Uniq added in v2.0.4

func Uniq[A any, K comparable](f func(A) K) Operator[A, A]

Uniq returns an operator that filters a sequence to contain only unique elements, where uniqueness is determined by a key extraction function.

This function takes a key extraction function and returns an operator that removes duplicate elements from a sequence. Two elements are considered duplicates if the key extraction function returns the same key for both. Only the first occurrence of each unique key is kept in the output sequence.

The operation maintains a map of seen keys internally, so memory usage grows with the number of unique keys encountered. The operation is lazy - elements are processed and filtered as they are consumed.

Marble Diagram:

Input:  --1--2--3--2--4--1--5-->
Uniq(identity)
Output: --1--2--3-----4-----5-->
        (first occurrence only)

RxJS Equivalent: [distinct] - https://rxjs.dev/api/operators/distinct

Type Parameters:

  • A: The type of elements in the sequence
  • K: The type of the key used for uniqueness comparison (must be comparable)

Parameters:

  • f: A function that extracts a comparable key from each element

Returns:

  • An Operator that filters the sequence to contain only unique elements based on the key

Example - Remove duplicate integers:

seq := From(1, 2, 3, 2, 4, 1, 5)
unique := Uniq(reader.Ask[int]())
result := unique(seq)
// yields: 1, 2, 3, 4, 5

Example - Unique by string length:

seq := From("a", "bb", "c", "dd", "eee")
uniqueByLength := Uniq(S.Size)
result := uniqueByLength(seq)
// yields: "a", "bb", "eee" (first occurrence of each length)

Example - Unique structs by field:

type Person struct { ID int; Name string }
seq := From(
    Person{1, "Alice"},
    Person{2, "Bob"},
    Person{1, "Alice2"},  // duplicate ID
)
uniqueByID := Uniq(func(p Person) int { return p.ID })
result := uniqueByID(seq)
// yields: Person{1, "Alice"}, Person{2, "Bob"}

Example - Case-insensitive unique strings:

seq := From("Hello", "world", "HELLO", "World", "test")
uniqueCaseInsensitive := Uniq(func(s string) string {
    return strings.ToLower(s)
})
result := uniqueCaseInsensitive(seq)
// yields: "Hello", "world", "test"

Example - Empty sequence:

seq := Empty[int]()
unique := Uniq(reader.Ask[int]())
result := unique(seq)
// yields: nothing (empty sequence)

Example - All duplicates:

seq := From(1, 1, 1, 1)
unique := Uniq(reader.Ask[int]())
result := unique(seq)
// yields: 1 (only first occurrence)
Example

Example tests for documentation

seq := From(1, 2, 3, 2, 4, 1, 5)
unique := Uniq(F.Identity[int])
result := unique(seq)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5
Example (ByLength)
seq := From("a", "bb", "c", "dd", "eee")
uniqueByLength := Uniq(S.Size)
result := uniqueByLength(seq)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
a bb eee
Example (CaseInsensitive)
seq := From("Hello", "world", "HELLO", "World", "test")
uniqueCaseInsensitive := Uniq(func(s string) string {
	return strings.ToLower(s)
})
result := uniqueCaseInsensitive(seq)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
Hello world test
Example (Empty)
seq := Empty[int]()
unique := Uniq(F.Identity[int])
result := unique(seq)

count := 0
for range result {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0

type Operator2

type Operator2[K, A, B any] = Kleisli2[K, Seq2[K, A], B]

Operator2 represents a transformation from one key-value sequence to another. It's a function that takes a Seq2[K, A] and returns a Seq2[K, B].

Type Parameters:

  • K: The key type (preserved in the transformation)
  • A: The value type of the input sequence
  • B: The value type of the output sequence

func FilterMapWithKey

func FilterMapWithKey[K, A, B any](f func(K, A) Option[B]) Operator2[K, A, B]

FilterMapWithKey returns a function that filters and maps key-value pairs. This is the curried version of MonadFilterMapWithKey.

Example:

largeDoubled := FilterMapWithKey(func(k string, v int) Option[int] {
    if v > 5 {
        return option.Some(v * 2)
    }
    return option.None[int]()
})
seq := Of2("x", 10)
result := largeDoubled(seq)
// yields: ("x", 20)

func FilterWithKey

func FilterWithKey[K, A any](pred func(K, A) bool) Operator2[K, A, A]

FilterWithKey returns a function that filters key-value pairs based on a predicate. This is the curried version of MonadFilterWithKey.

Example:

largeValues := FilterWithKey(func(k string, v int) bool { return v > 5 })
seq := Of2("x", 10)
result := largeValues(seq)
// yields: ("x", 10)

func MapWithKey

func MapWithKey[K, A, B any](f func(K, A) B) Operator2[K, A, B]

MapWithKey returns a function that transforms values using their keys. This is the curried version of MonadMapWithKey.

Example:

doubleValue := MapWithKey(func(k string, v int) int { return v * 2 })
seq := Of2("x", 10)
result := doubleValue(seq)
// yields: ("x", 20)

type Option

type Option[A any] = option.Option[A]

Option represents an optional value, either Some(value) or None. It is used to handle computations that may or may not return a value, providing a type-safe alternative to nil pointers or sentinel values.

Type Parameters:

  • A: The type of the value that may be present

func First added in v2.0.3

func First[U any](mu Seq[U]) Option[U]

First returns the first element from an Iterator wrapped in an Option.

This function attempts to retrieve the first element from the iterator. If the iterator contains at least one element, it returns Some(element). If the iterator is empty, it returns None. The function consumes only the first element of the iterator.

Marble Diagram:

Input:  --1--2--3--4--5-->
First
Output: --Some(1)|

Input:  --|
First
Output: --None|

RxJS Equivalent: [first] - https://rxjs.dev/api/operators/first

Type Parameters:

  • U: The type of elements in the iterator

Parameters:

  • mu: The input iterator to get the first element from

Returns:

  • Option[U]: Some(first element) if the iterator is non-empty, None otherwise

Example with non-empty sequence:

seq := iter.From(1, 2, 3, 4, 5)
first := iter.First(seq)
// Returns: Some(1)

Example with empty sequence:

seq := iter.Empty[int]()
first := iter.First(seq)
// Returns: None

Example with filtered sequence:

seq := iter.From(1, 2, 3, 4, 5)
filtered := iter.Filter(func(x int) bool { return x > 3 })(seq)
first := iter.First(filtered)
// Returns: Some(4)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
first := First(seq)

if value, ok := O.Unwrap(first); ok {
	fmt.Printf("First element: %d\n", value)
}
Output:
First element: 1
Example (Empty)
seq := Empty[int]()
first := First(seq)

if _, ok := O.Unwrap(first); !ok {
	fmt.Println("Sequence is empty")
}
Output:
Sequence is empty
Example (Filtered)
seq := From(1, 2, 3, 4, 5)
filtered := MonadFilter(seq, N.MoreThan(3))
first := First(filtered)

if value, ok := O.Unwrap(first); ok {
	fmt.Printf("First element > 3: %d\n", value)
}
Output:
First element > 3: 4

func Last added in v2.1.1

func Last[U any](it Seq[U]) Option[U]

Last returns the last element from an Iterator wrapped in an Option.

This function retrieves the last element from the iterator by consuming the entire sequence. If the iterator contains at least one element, it returns Some(element). If the iterator is empty, it returns None.

Marble Diagram:

Input:  --1--2--3--4--5--|
Last
Output: -----------------Some(5)|

Input:  --|
Last
Output: --None|

RxJS Equivalent: [last] - https://rxjs.dev/api/operators/last

Type Parameters:

  • U: The type of elements in the iterator

Parameters:

  • it: The input iterator to get the last element from

Returns:

  • Option[U]: Some(last element) if the iterator is non-empty, None otherwise

Example with non-empty sequence:

seq := iter.From(1, 2, 3, 4, 5)
last := iter.Last(seq)
// Returns: Some(5)

Example with empty sequence:

seq := iter.Empty[int]()
last := iter.Last(seq)
// Returns: None

Example with filtered sequence:

seq := iter.From(1, 2, 3, 4, 5)
filtered := iter.Filter(func(x int) bool { return x < 4 })(seq)
last := iter.Last(filtered)
// Returns: Some(3)
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
last := Last(seq)

if value, ok := O.Unwrap(last); ok {
	fmt.Printf("Last element: %d\n", value)
}
Output:
Last element: 5
Example (Empty)
seq := Empty[int]()
last := Last(seq)

if _, ok := O.Unwrap(last); !ok {
	fmt.Println("Sequence is empty")
}
Output:
Sequence is empty
Example (Functions)
f1 := function.Constant("first")
f2 := function.Constant("middle")
f3 := function.Constant("last")
seq := From(f1, f2, f3)

last := Last(seq)

if fn, ok := O.Unwrap(last); ok {
	result := fn()
	fmt.Printf("Last function result: %s\n", result)
}
Output:
Last function result: last

type Pair added in v2.0.3

type Pair[A, B any] = pair.Pair[A, B]

Pair represents a tuple of two values. It's used to group two related values together.

Type Parameters:

  • A: The type of the first element
  • B: The type of the second element

Example:

p := pair.MakePair(1, "hello")
first := pair.Head(p)  // returns 1
second := pair.Tail(p) // returns "hello"

type Predicate

type Predicate[T any] = predicate.Predicate[T]

Predicate is a function that tests a value and returns a boolean. Predicates are commonly used for filtering operations.

Type Parameters:

  • T: The type of value being tested

Example:

isEven := func(x int) bool { return x%2 == 0 }
filtered := Filter(isEven)(From(1, 2, 3, 4))

type Prism added in v2.0.3

type Prism[S, A any] = prism.Prism[S, A]

Prism is an optic that focuses on a case of a sum type. It provides a functional way to work with variant types (like Result or Option).

Type Parameters:

  • S: The sum type
  • A: The case type being focused on

type Seq

type Seq[T any] = I.Seq[T]

Seq is a single-value iterator sequence from Go 1.23+. It represents a lazy sequence of values that can be iterated using range. Operations on Seq are lazy and only execute when the sequence is consumed.

Type Parameters:

  • T: The type of elements in the sequence

Example:

seq := From(1, 2, 3)
for v := range seq {
    fmt.Println(v)
}

func Async added in v2.2.62

func Async[T any](input Seq[T]) Seq[T]

Async converts a synchronous sequence into an asynchronous sequence using a default buffer size. This is a convenience wrapper around AsyncBuf that uses a default buffer size of 8.

Type Parameters:

  • T: The type of elements in the sequence

Parameters:

  • input: The source sequence to be consumed asynchronously

Returns:

  • Seq[T]: A new sequence that yields elements from the input sequence asynchronously

Behavior:

  • Uses a default buffer size of 8 for the internal channel
  • Spawns a goroutine that consumes the input sequence
  • Elements are sent through a buffered channel to the output sequence
  • Properly handles early termination with goroutine cleanup
  • The channel is closed when the input sequence is exhausted

Example:

seq := From(1, 2, 3, 4, 5)
async := Async(seq)

// Elements are produced concurrently
for v := range async {
    fmt.Println(v) // Prints: 1, 2, 3, 4, 5
}

See Also:

  • AsyncBuf: Async with custom buffer size
  • Async2: Asynchronous sequence for key-value sequences
  • Merge: Merges multiple sequences concurrently
Example

Example tests for documentation

seq := From(1, 2, 3, 4, 5)
async := AsyncBuf(seq, 10)

for v := range async {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5
Example (EarlyTermination)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
async := AsyncBuf(seq, 5)

count := 0
for v := range async {
	fmt.Printf("%d ", v)
	count++
	if count >= 3 {
		break
	}
}
Output:
1 2 3
Example (Unbuffered)
seq := From(1, 2, 3)
async := AsyncBuf(seq, 0)

for v := range async {
	fmt.Printf("%d ", v)
}
Output:
1 2 3
Example (WithFilter)
seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
async := AsyncBuf(seq, 5)
evens := MonadFilter(async, func(x int) bool { return x%2 == 0 })

for v := range evens {
	fmt.Printf("%d ", v)
}
Output:
2 4 6 8 10
Example (WithMap)
seq := From(1, 2, 3, 4, 5)
async := AsyncBuf(seq, 5)
doubled := MonadMap(async, N.Mul(2))

for v := range doubled {
	fmt.Printf("%d ", v)
}
Output:
2 4 6 8 10

func AsyncBuf added in v2.2.72

func AsyncBuf[T any](input Seq[T], bufSize int) Seq[T]

AsyncBuf converts a synchronous sequence into an asynchronous buffered sequence. It spawns a goroutine to consume the input sequence and sends values through a buffered channel, allowing concurrent production and consumption of elements.

The function provides backpressure control through the buffer size and properly handles early termination when the consumer stops iterating. This is useful for decoupling producers and consumers, enabling pipeline parallelism, or when you need to process sequences concurrently.

Type Parameters

  • T: The type of elements in the sequence

Parameters

  • input: The source sequence to be consumed asynchronously
  • bufSize: The buffer size for the channel. Negative values are treated as 0 (unbuffered). A larger buffer allows more elements to be produced ahead of consumption, but uses more memory. A buffer of 0 creates an unbuffered channel requiring synchronization between producer and consumer.

Returns

  • Seq[T]: A new sequence that yields elements from the input sequence asynchronously

Behavior

  • Spawns a goroutine that consumes the input sequence
  • Elements are sent through a buffered channel to the output sequence
  • Properly handles early termination: if the consumer stops iterating (yield returns false), the producer goroutine is signaled to stop via a done channel
  • Both the producer goroutine and the done channel are properly cleaned up
  • The channel is closed when the input sequence is exhausted or early termination occurs

Example Usage

// Create an async sequence with a buffer of 10
seq := From(1, 2, 3, 4, 5)
async := AsyncBuf(seq, 10)

// Elements are produced concurrently
for v := range async {
    fmt.Println(v) // Prints: 1, 2, 3, 4, 5
}

Example with Early Termination

seq := From(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
async := AsyncBuf(seq, 5)

// Stop after 3 elements - producer goroutine will be properly cleaned up
count := 0
for v := range async {
    fmt.Println(v)
    count++
    if count >= 3 {
        break
    }
}

Example with Unbuffered Channel

// bufSize of 0 creates an unbuffered channel
seq := From(1, 2, 3)
async := AsyncBuf(seq, 0)

// Producer and consumer are synchronized
for v := range async {
    fmt.Println(v)
}

See Also

  • From: Creates a sequence from values
  • Map: Transforms sequence elements
  • Filter: Filters sequence elements

func ConcatAll added in v2.2.72

func ConcatAll[A any](mma Seq[Seq[A]]) Seq[A]

func Cycle added in v2.0.4

func Cycle[U any](ma Seq[U]) Seq[U]

Cycle creates a sequence that repeats the elements of the input sequence indefinitely.

This function takes a finite sequence and creates an infinite sequence by cycling through all elements repeatedly. When the end of the input sequence is reached, it starts over from the beginning, continuing this pattern forever.

Marble Diagram:

Input:  --1--2--3|
Cycle
Output: --1--2--3--1--2--3--1--2--3--> (infinite)

RxJS Equivalent: [repeat] - https://rxjs.dev/api/operators/repeat

WARNING: This creates an INFINITE sequence for non-empty inputs. It must be used with operations that limit the output (such as Take, First, or early termination in iteration) to avoid infinite loops.

If the input sequence is empty, Cycle returns an empty sequence immediately. It does NOT loop indefinitely - the result is simply an empty sequence.

The operation is lazy - elements are only generated as they are consumed. The input sequence is re-iterated each time the cycle completes, so any side effects in the source sequence will be repeated.

Type Parameters:

  • U: The type of elements in the sequence

Parameters:

  • ma: The input sequence to cycle through. Should be finite.

Returns:

  • An infinite sequence that repeats the elements of the input sequence

Example - Basic cycling with Take:

seq := From(1, 2, 3)
cycled := Cycle(seq)
result := Take[int](7)(cycled)
// yields: 1, 2, 3, 1, 2, 3, 1

Example - Cycling strings:

seq := From("A", "B", "C")
cycled := Cycle(seq)
result := Take[string](5)(cycled)
// yields: "A", "B", "C", "A", "B"

Example - Using with First:

seq := From(10, 20, 30)
cycled := Cycle(seq)
first := First(cycled)
// returns: Some(10)

Example - Combining with filter and take:

seq := From(1, 2, 3, 4, 5)
cycled := Cycle(seq)
evens := MonadFilter(cycled, func(x int) bool { return x%2 == 0 })
result := Take[int](5)(evens)
// yields: 2, 4, 2, 4, 2 (cycles through even numbers)

Example - Empty sequence (returns empty, does not loop):

seq := Empty[int]()
cycled := Cycle(seq)
result := Take[int](10)(cycled)
// yields: nothing (empty sequence, terminates immediately)
Example

Example tests for documentation

seq := From(1, 2, 3)
cycled := Cycle(seq)
result := Take[int](7)(cycled)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 1 2 3 1
Example (Empty)
seq := Empty[int]()
cycled := Cycle(seq)
result := Take[int](5)(cycled)

count := 0
for range result {
	count++
}
fmt.Printf("Count: %d\n", count)
Output:
Count: 0
Example (ExactMultiple)
seq := From("A", "B", "C")
cycled := Cycle(seq)
result := Take[string](9)(cycled)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
A B C A B C A B C
Example (SingleElement)
seq := From("X")
cycled := Cycle(seq)
result := Take[string](5)(cycled)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
X X X X X
Example (WithFilter)
seq := From(1, 2, 3, 4, 5)
cycled := Cycle(seq)
evens := MonadFilter(cycled, func(x int) bool { return x%2 == 0 })
result := Take[int](6)(evens)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
2 4 2 4 2 4
Example (WithFirst)
seq := From(10, 20, 30)
cycled := Cycle(seq)
first := First(cycled)

if value, ok := O.Unwrap(first); ok {
	fmt.Printf("First: %d\n", value)
}
Output:
First: 10
Example (WithMap)
seq := From(1, 2, 3)
cycled := Cycle(seq)
doubled := MonadMap(cycled, N.Mul(2))
result := Take[int](7)(doubled)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
2 4 6 2 4 6 2

func Do added in v2.0.3

func Do[S any](
	empty S,
) Seq[S]

Do creates a sequence containing a single element, typically used to start a do-notation chain. This is the entry point for monadic composition using do-notation style.

Type Parameters:

  • S: The type of the state/structure being built

Parameters:

  • empty: The initial value to wrap in a sequence

Returns:

  • A sequence containing the single element

Example:

type User struct {
    Name string
    Age  int
}

// Start a do-notation chain
result := Do(User{})
// yields: User{Name: "", Age: 0}
Example

Example tests for documentation

result := Do(42)
for v := range result {
	fmt.Println(v)
}
Output:
42

func Empty

func Empty[A any]() Seq[A]

Empty returns an empty sequence that yields no elements.

Example:

seq := Empty[int]()
// yields nothing

func Flatten

func Flatten[A any](mma Seq[Seq[A]]) Seq[A]

Flatten flattens a sequence of sequences into a single sequence.

Marble Diagram:

Input:  --[1,2]--[3,4]--[5]-->
Flatten
Output: --1-2----3-4----5---->

RxJS Equivalent: [mergeAll] - https://rxjs.dev/api/operators/mergeAll

Example:

nested := From(From(1, 2), From(3, 4), From(5))
result := Flatten(nested)
// yields: 1, 2, 3, 4, 5

func From

func From[A any](data ...A) Seq[A]

From creates a sequence from a variadic list of elements.

Example:

seq := From(1, 2, 3, 4, 5)
// yields: 1, 2, 3, 4, 5

func MakeBy

func MakeBy[A any](n int, f func(int) A) Seq[A]

MakeBy creates a sequence of n elements by applying a function to each index. Returns an empty sequence if n <= 0.

Example:

seq := MakeBy(5, func(i int) int { return i * i })
// yields: 0, 1, 4, 9, 16

func Merge added in v2.2.72

func Merge[T any](iterables []Seq[T]) Seq[T]

Merge merges multiple sequences concurrently into a single sequence using a default buffer size. This is a convenience wrapper around MergeBuf that uses a default buffer size of 8.

Type Parameters:

  • T: The type of elements in the sequences

Parameters:

  • iterables: A slice of sequences to merge. If empty, returns an empty sequence.

Returns:

  • Seq[T]: A new sequence that yields elements from all input sequences in non-deterministic order

Behavior:

  • Uses a default buffer size of 8 for the internal channel
  • Spawns one goroutine per input sequence to produce elements concurrently
  • Elements from different sequences are interleaved non-deterministically
  • Properly handles early termination with goroutine cleanup
  • Thread-safe: multiple producers can safely send to the shared channel

Example:

seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
seq3 := From(7, 8, 9)
merged := Merge([]Seq[int]{seq1, seq2, seq3})

// Elements appear in non-deterministic order
for v := range merged {
    fmt.Println(v) // May print: 1, 4, 7, 2, 5, 8, 3, 6, 9 (order varies)
}

See Also:

  • MergeBuf: Merge with custom buffer size
  • MergeAll: Merges a sequence of sequences
  • Async: Converts a single sequence to asynchronous
Example

Example tests for Merge

seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 10)

result := toSlice(merged)
slices.Sort(result)
for _, v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5 6

func MergeBuf added in v2.2.72

func MergeBuf[T any](iterables []Seq[T], bufSize int) Seq[T]

MergeBuf merges multiple sequences concurrently into a single sequence. It spawns a goroutine for each input sequence and merges their elements through a buffered channel, allowing concurrent production from all sources. The output order is non-deterministic and depends on the timing of concurrent producers.

This function is useful for combining results from multiple concurrent operations, processing data from multiple sources in parallel, or implementing fan-in patterns where multiple producers feed into a single consumer.

Type Parameters:

  • T: The type of elements in the sequences

Parameters:

  • iterables: A slice of sequences to merge. If empty, returns an empty sequence.
  • bufSize: The buffer size for the internal channel. Negative values are treated as 0 (unbuffered). A larger buffer allows more elements to be produced ahead of consumption, reducing contention between producers but using more memory. A buffer of 0 creates an unbuffered channel requiring synchronization.

Returns:

  • Seq[T]: A new sequence that yields elements from all input sequences in non-deterministic order

Behavior:

  • Spawns one goroutine per input sequence to produce elements concurrently
  • Elements from different sequences are interleaved non-deterministically
  • Properly handles early termination: if the consumer stops iterating (yield returns false), all producer goroutines are signaled to stop and cleaned up
  • The output channel is closed when all input sequences are exhausted
  • No goroutines leak even with early termination
  • Thread-safe: multiple producers can safely send to the shared channel

Example Usage:

// MergeBuf three sequences concurrently
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
seq3 := From(7, 8, 9)
merged := MergeBuf([]Seq[int]{seq1, seq2, seq3}, 10)

// Elements appear in non-deterministic order
for v := range merged {
    fmt.Println(v) // May print: 1, 4, 7, 2, 5, 8, 3, 6, 9 (order varies)
}

Example with Early Termination:

seq1 := From(1, 2, 3, 4, 5)
seq2 := From(6, 7, 8, 9, 10)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 5)

// Stop after 3 elements - all producer goroutines will be properly cleaned up
count := 0
for v := range merged {
    fmt.Println(v)
    count++
    if count >= 3 {
        break
    }
}

Example with Unbuffered Channel:

// bufSize of 0 creates an unbuffered channel
seq1 := From(1, 2, 3)
seq2 := From(4, 5, 6)
merged := MergeBuf([]Seq[int]{seq1, seq2}, 0)

// Producers and consumer are synchronized
for v := range merged {
    fmt.Println(v)
}

See Also:

  • Async: Converts a single sequence to asynchronous
  • From: Creates a sequence from values
  • MonadChain: Sequentially chains sequences (deterministic order)

func MonadAp

func MonadAp[B, A any](fab Seq[func(A) B], fa Seq[A]) Seq[B]

MonadAp applies a sequence of functions to a sequence of values. This is the applicative apply operation.

Marble Diagram:

Functions: --(*2)---(+10)-->
Values:    --5------3------>
Ap
Output:    --10-6---15-13-->
           (each function applied to each value)

Example:

fns := From(N.Mul(2), N.Add(10))
vals := From(5, 3)
result := MonadAp(fns, vals)
// yields: 10, 6, 15, 13 (each function applied to each value)

func MonadChain

func MonadChain[A, B any](as Seq[A], f Kleisli[A, B]) Seq[B]

MonadChain applies a function that returns a sequence to each element and flattens the results. This is the monadic bind operation (flatMap).

Marble Diagram:

Input:  --1-----2-----3---->
Chain(x => [x, x*10])
Output: --1-10--2-20--3-30->

RxJS Equivalent: mergeMap/flatMap - https://rxjs.dev/api/operators/mergeMap

Example:

seq := From(1, 2, 3)
result := MonadChain(seq, func(x int) Seq[int] {
    return From(x, x*10)
})
// yields: 1, 10, 2, 20, 3, 30

func MonadChainOptionK added in v2.2.16

func MonadChainOptionK[A, B any](as Seq[A], f option.Kleisli[A, B]) Seq[B]

MonadChainOptionK chains a function that returns an Option into a sequence, filtering out None values and unwrapping Some values.

This is useful for operations that may or may not produce a value for each element in the sequence. Only the successful (Some) results are included in the output sequence, while None values are filtered out.

This is the monadic form that takes the sequence as the first parameter.

Marble Diagram:

Input:  --1--2--3--4--5-->
ChainOptionK(x => x % 2 == 0 ? Some(x * 10) : None)
Output: -----20----40---->
        (filters and transforms)

RxJS Equivalent: [concatMap] combined with [filter] - https://rxjs.dev/api/operators/concatMap

Type parameters:

  • A: The element type of the input sequence
  • B: The element type of the output sequence (wrapped in Option by the function)

Parameters:

  • as: The input sequence to transform
  • f: A function that takes an element and returns an Option[B]

Returns:

A new sequence containing only the unwrapped Some values

Example:

import (
    "strconv"
    F "github.com/IBM/fp-go/v2/function"
    O "github.com/IBM/fp-go/v2/option"
    I "github.com/IBM/fp-go/v2/iterator/iter"
)

// Parse strings to integers, filtering out invalid ones
parseNum := func(s string) O.Option[int] {
    if n, err := strconv.Atoi(s); err == nil {
        return O.Some(n)
    }
    return O.None[int]()
}

seq := I.From("1", "invalid", "2", "3", "bad")
result := I.MonadChainOptionK(seq, parseNum)
// yields: 1, 2, 3 (invalid strings are filtered out)

func MonadFilter

func MonadFilter[A any](as Seq[A], pred func(A) bool) Seq[A]

MonadFilter returns a sequence containing only elements that satisfy the predicate.

Marble Diagram:

Input:  --1--2--3--4--5-->
Filter(x => x % 2 == 0)
Output: -----2-----4----->

RxJS Equivalent: [filter] - https://rxjs.dev/api/operators/filter

Example:

seq := From(1, 2, 3, 4, 5)
result := MonadFilter(seq, func(x int) bool { return x%2 == 0 })
// yields: 2, 4

func MonadFilterMap

func MonadFilterMap[A, B any](as Seq[A], f option.Kleisli[A, B]) Seq[B]

MonadFilterMap applies a function that returns an Option to each element, keeping only the Some values and unwrapping them.

Marble Diagram:

Input:  --1--2--3--4--5-->
FilterMap(x => x % 2 == 0 ? Some(x * 10) : None)
Output: -----20----40---->

Example:

seq := From(1, 2, 3, 4, 5)
result := MonadFilterMap(seq, func(x int) Option[int] {
    if x%2 == 0 {
        return option.Some(x * 10)
    }
    return option.None[int]()
})
// yields: 20, 40

func MonadFilterMapWithIndex

func MonadFilterMapWithIndex[A, B any](as Seq[A], f func(int, A) Option[B]) Seq[B]

MonadFilterMapWithIndex applies a function with index that returns an Option, keeping only the Some values.

Example:

seq := From("a", "b", "c")
result := MonadFilterMapWithIndex(seq, func(i int, s string) Option[string] {
    if i%2 == 0 {
        return option.Some(fmt.Sprintf("%d:%s", i, s))
    }
    return option.None[string]()
})
// yields: "0:a", "2:c"

func MonadFilterWithIndex

func MonadFilterWithIndex[A any](as Seq[A], pred func(int, A) bool) Seq[A]

MonadFilterWithIndex filters elements using a predicate that also receives the element's index.

Example:

seq := From("a", "b", "c", "d")
result := MonadFilterWithIndex(seq, func(i int, s string) bool { return i%2 == 0 })
// yields: "a", "c" (elements at even indices)

func MonadFlap

func MonadFlap[B, A any](fab Seq[func(A) B], a A) Seq[B]

MonadFlap applies a fixed value to a sequence of functions. This is the dual of MonadAp.

Marble Diagram:

Functions: --(*2)---(+10)-->
Value:     5 (fixed)
Flap
Output:    --10-----15----->

Example:

fns := From(N.Mul(2), N.Add(10))
result := MonadFlap(fns, 5)
// yields: 10, 15

func MonadMap

func MonadMap[A, B any](as Seq[A], f func(A) B) Seq[B]

MonadMap transforms each element in a sequence using the provided function. This is the monadic version that takes the sequence as the first parameter.

Marble Diagram:

Input:  --1--2--3-->
Map(x => x * 2)
Output: --2--4--6-->

RxJS Equivalent: [map] - https://rxjs.dev/api/operators/map

Example:

seq := From(1, 2, 3)
result := MonadMap(seq, N.Mul(2))
// yields: 2, 4, 6

func MonadMapWithIndex

func MonadMapWithIndex[A, B any](as Seq[A], f func(int, A) B) Seq[B]

MonadMapWithIndex transforms each element in a sequence using a function that also receives the element's index.

Example:

seq := From("a", "b", "c")
result := MonadMapWithIndex(seq, func(i int, s string) string {
    return fmt.Sprintf("%d:%s", i, s)
})
// yields: "0:a", "1:b", "2:c"

func Of

func Of[A any](a A) Seq[A]

Of creates a sequence containing a single element.

Example:

seq := Of(42)
// yields: 42

func Replicate

func Replicate[A any](n int, a A) Seq[A]

Replicate creates a sequence containing n copies of the same element.

Example:

seq := Replicate(3, "hello")
// yields: "hello", "hello", "hello"

func StrictUniq added in v2.0.4

func StrictUniq[A comparable](as Seq[A]) Seq[A]

StrictUniq filters a sequence to contain only unique elements using direct comparison.

This is a convenience function that uses the identity function as the key extractor, meaning elements are compared directly for uniqueness. It's equivalent to calling Uniq with the identity function, but provides a simpler API when the elements themselves are comparable.

The operation maintains a map of seen elements internally, so memory usage grows with the number of unique elements. Only the first occurrence of each unique element is kept.

Marble Diagram:

Input:  --1--2--3--2--4--1--5-->
StrictUniq
Output: --1--2--3-----4-----5-->
        (first occurrence only)

RxJS Equivalent: [distinct] - https://rxjs.dev/api/operators/distinct

Type Parameters:

  • A: The type of elements in the sequence (must be comparable)

Parameters:

  • as: The input sequence to filter for unique elements

Returns:

  • A sequence containing only the first occurrence of each unique element

Example - Remove duplicate integers:

seq := From(1, 2, 3, 2, 4, 1, 5)
result := StrictUniq(seq)
// yields: 1, 2, 3, 4, 5

Example - Remove duplicate strings:

seq := From("apple", "banana", "apple", "cherry", "banana")
result := StrictUniq(seq)
// yields: "apple", "banana", "cherry"

Example - Single element:

seq := From(42)
result := StrictUniq(seq)
// yields: 42

Example - All duplicates:

seq := From("x", "x", "x")
result := StrictUniq(seq)
// yields: "x" (only first occurrence)

Example - Empty sequence:

seq := Empty[int]()
result := StrictUniq(seq)
// yields: nothing (empty sequence)

Example - Already unique:

seq := From(1, 2, 3, 4, 5)
result := StrictUniq(seq)
// yields: 1, 2, 3, 4, 5 (no changes)
Example
seq := From(1, 2, 3, 2, 4, 1, 5)
result := StrictUniq(seq)

for v := range result {
	fmt.Printf("%d ", v)
}
Output:
1 2 3 4 5
Example (Strings)
seq := From("apple", "banana", "apple", "cherry", "banana")
result := StrictUniq(seq)

for v := range result {
	fmt.Printf("%s ", v)
}
Output:
apple banana cherry

func ToSeqPair added in v2.0.3

func ToSeqPair[A, B any](as Seq2[A, B]) Seq[Pair[A, B]]

ToSeqPair converts a key-value sequence (Seq2) into a sequence of Pairs.

This function transforms a Seq2[A, B] (which yields key-value pairs when iterated) into a Seq[Pair[A, B]] (which yields Pair objects). This is useful when you need to work with pairs as first-class values rather than as separate key-value arguments.

Type Parameters:

  • A: The type of the first element (key) in each pair
  • B: The type of the second element (value) in each pair

Parameters:

  • as: A Seq2 that yields key-value pairs

Returns:

  • A Seq that yields Pair objects containing the key-value pairs

Example - Basic conversion:

seq2 := iter.MonadZip(iter.From("a", "b", "c"), iter.From(1, 2, 3))
pairs := iter.ToSeqPair(seq2)
// yields: Pair("a", 1), Pair("b", 2), Pair("c", 3)

Example - Using with Map:

seq2 := iter.MonadZip(iter.From(1, 2, 3), iter.From(10, 20, 30))
pairs := iter.ToSeqPair(seq2)
sums := iter.MonadMap(pairs, func(p Pair[int, int]) int {
    return p.Fst + p.Snd
})
// yields: 11, 22, 33

Example - Empty sequence:

seq2 := iter.Empty[int]()
zipped := iter.MonadZip(seq2, iter.Empty[string]())
pairs := iter.ToSeqPair(zipped)
// yields: nothing (empty sequence)
Example
seq2 := MonadZip(From(1, 2, 3), From("a", "b", "c"))
pairs := ToSeqPair(seq2)

for p := range pairs {
	fmt.Printf("(%d, %s) ", P.Head(p), P.Tail(p))
}
Output:
(1, a) (2, b) (3, c)
Example (WithMap)
seq2 := MonadZip(From(1, 2, 3), From(10, 20, 30))
pairs := ToSeqPair(seq2)
sums := MonadMap(pairs, func(p Pair[int, int]) int {
	return P.Head(p) + P.Tail(p)
})

for sum := range sums {
	fmt.Printf("%d ", sum)
}
Output:
11 22 33

type Seq2

type Seq2[K, V any] = I.Seq2[K, V]

Seq2 is a key-value iterator sequence from Go 1.23+. It represents a lazy sequence of key-value pairs that can be iterated using range. This is useful for working with map-like data structures in a functional way.

Type Parameters:

  • K: The type of keys in the sequence
  • V: The type of values in the sequence

Example:

seq := MonadZip(From(1, 2, 3), From("a", "b", "c"))
for k, v := range seq {
    fmt.Printf("%d: %s\n", k, v)
}

func Async2 added in v2.2.62

func Async2[K, V any](input Seq2[K, V]) Seq2[K, V]

Async2 converts a synchronous key-value sequence into an asynchronous sequence using a default buffer size. This is a convenience wrapper around Async2Buf that uses a default buffer size of 8. It's the Seq2 variant of Async, providing the same asynchronous behavior for key-value sequences.

Type Parameters:

  • K: The type of keys in the sequence
  • V: The type of values in the sequence

Parameters:

  • input: The source key-value sequence to be consumed asynchronously

Returns:

  • Seq2[K, V]: A new key-value sequence that yields elements from the input sequence asynchronously

Behavior:

  • Uses a default buffer size of 8 for the internal channel
  • Spawns a goroutine that consumes the input key-value sequence
  • Key-value pairs are sent through a buffered channel to the output sequence
  • Properly handles early termination with goroutine cleanup
  • The channel is closed when the input sequence is exhausted

Example:

seq := MonadZip(From(1, 2, 3), From("a", "b", "c"))
async := Async2(seq)

// Elements are produced concurrently
for k, v := range async {
    fmt.Printf("%d: %s\n", k, v)
}
// Output:
// 1: a
// 2: b
// 3: c

See Also:

  • Async2Buf: Async2 with custom buffer size
  • Async: Asynchronous sequence for single-value sequences
  • MonadZip: Creates key-value sequences from two sequences
Example

Example tests for Async2

seq := MonadZip(From(1, 2, 3), From("a", "b", "c"))
async := Async2Buf(seq, 10)

for k, v := range async {
	fmt.Printf("%d: %s\n", k, v)
}
Output:
1: a
2: b
3: c
Example (EarlyTermination)
seq := MonadZip(From(1, 2, 3, 4, 5), From("a", "b", "c", "d", "e"))
async := Async2Buf(seq, 5)

count := 0
for k, v := range async {
	fmt.Printf("%d: %s\n", k, v)
	count++
	if count >= 2 {
		break
	}
}
Output:
1: a
2: b

func Async2Buf added in v2.2.72

func Async2Buf[K, V any](input Seq2[K, V], bufSize int) Seq2[K, V]

Async2Buf converts a synchronous key-value sequence into an asynchronous buffered sequence. It spawns a goroutine to consume the input sequence and sends key-value pairs through a buffered channel, allowing concurrent production and consumption of elements.

This function is the Seq2 variant of Async, providing the same asynchronous behavior for key-value sequences. It internally converts the Seq2 to a sequence of Pairs, applies Async, and converts back to Seq2.

Type Parameters

  • K: The type of keys in the sequence
  • V: The type of values in the sequence

Parameters

  • input: The source key-value sequence to be consumed asynchronously
  • bufSize: The buffer size for the channel. Negative values are treated as 0 (unbuffered). A larger buffer allows more elements to be produced ahead of consumption, but uses more memory. A buffer of 0 creates an unbuffered channel requiring synchronization between producer and consumer.

Returns

  • Seq2[K, V]: A new key-value sequence that yields elements from the input sequence asynchronously

Behavior

  • Spawns a goroutine that consumes the input key-value sequence
  • Key-value pairs are sent through a buffered channel to the output sequence
  • Properly handles early termination: if the consumer stops iterating (yield returns false), the producer goroutine is signaled to stop via a done channel
  • Both the producer goroutine and the done channel are properly cleaned up
  • The channel is closed when the input sequence is exhausted or early termination occurs

Example Usage

// Create an async key-value sequence with a buffer of 10
seq := MonadZip(From(1, 2, 3), From("a", "b", "c"))
async := Async2Buf(seq, 10)

// Elements are produced concurrently
for k, v := range async {
    fmt.Printf("%d: %s\n", k, v)
}
// Output:
// 1: a
// 2: b
// 3: c

Example with Early Termination

seq := MonadZip(From(1, 2, 3, 4, 5), From("a", "b", "c", "d", "e"))
async := Async2Buf(seq, 5)

// Stop after 2 pairs - producer goroutine will be properly cleaned up
count := 0
for k, v := range async {
    fmt.Printf("%d: %s\n", k, v)
    count++
    if count >= 2 {
        break
    }
}

See Also

  • Async: Asynchronous sequence for single-value sequences
  • ToSeqPair: Converts Seq2 to Seq of Pairs
  • FromSeqPair: Converts Seq of Pairs to Seq2
  • MonadZip: Creates key-value sequences from two sequences

func FromSeqPair added in v2.2.62

func FromSeqPair[A, B any](as Seq[Pair[A, B]]) Seq2[A, B]

FromSeqPair converts a sequence of Pairs into a key-value sequence.

This function transforms a Seq[Pair[A, B]] (which yields Pair objects when iterated) into a Seq2[A, B] (which yields key-value pairs as separate arguments). This is the inverse operation of ToSeqPair and is useful when you need to convert from working with pairs as first-class values back to the key-value iteration pattern.

Type Parameters

  • A: The type of the first element (key) in each pair
  • B: The type of the second element (value) in each pair

Parameters

  • as: A Seq that yields Pair objects

Returns

  • Seq2[A, B]: A key-value sequence that yields the unpacked pairs

Example Usage

// Create a sequence of pairs
pairs := From(
    pair.MakePair("a", 1),
    pair.MakePair("b", 2),
    pair.MakePair("c", 3),
)
seq2 := FromSeqPair(pairs)

// Iterate as key-value pairs
for k, v := range seq2 {
    fmt.Printf("%s: %d\n", k, v)
}
// Output:
// a: 1
// b: 2
// c: 3

Example with Map

pairs := From(
    pair.MakePair(1, 10),
    pair.MakePair(2, 20),
    pair.MakePair(3, 30),
)
seq2 := FromSeqPair(pairs)

// Use with Seq2 operations
mapped := MonadMapWithKey(seq2, func(k, v int) int {
    return k + v
})
// yields: 11, 22, 33

Example - Round-trip conversion

original := MonadZip(From(1, 2, 3), From("a", "b", "c"))
pairs := ToSeqPair(original)
restored := FromSeqPair(pairs)
// restored is equivalent to original

See Also

  • ToSeqPair: Converts Seq2 to Seq of Pairs (inverse operation)
  • MonadZip: Creates key-value sequences from two sequences
  • pair.MakePair: Creates a Pair from two values
  • pair.Unpack: Unpacks a Pair into two values
Example

Example tests for FromSeqPair

pairs := From(
	pair.MakePair(1, "a"),
	pair.MakePair(2, "b"),
	pair.MakePair(3, "c"),
)
seq2 := FromSeqPair(pairs)

for k, v := range seq2 {
	fmt.Printf("%d: %s\n", k, v)
}
Output:
1: a
2: b
3: c

func MonadFilterMapWithKey

func MonadFilterMapWithKey[K, A, B any](as Seq2[K, A], f func(K, A) Option[B]) Seq2[K, B]

MonadFilterMapWithKey applies a function with key that returns an Option to key-value pairs, keeping only the Some values.

Example:

seq := Of2("x", 10)
result := MonadFilterMapWithKey(seq, func(k string, v int) Option[int] {
    if v > 5 {
        return option.Some(v * 2)
    }
    return option.None[int]()
})
// yields: ("x", 20)

func MonadFilterWithKey

func MonadFilterWithKey[K, A any](as Seq2[K, A], pred func(K, A) bool) Seq2[K, A]

MonadFilterWithKey filters key-value pairs using a predicate that receives both key and value.

Example:

seq := Of2("x", 10)
result := MonadFilterWithKey(seq, func(k string, v int) bool { return v > 5 })
// yields: ("x", 10)

func MonadMapWithKey

func MonadMapWithKey[K, A, B any](as Seq2[K, A], f func(K, A) B) Seq2[K, B]

MonadMapWithKey transforms values in a key-value sequence using a function that receives both key and value.

Example:

seq := Of2("x", 10)
result := MonadMapWithKey(seq, func(k string, v int) int { return v * 2 })
// yields: ("x", 20)

func MonadZip

func MonadZip[A, B any](fa Seq[A], fb Seq[B]) Seq2[A, B]

MonadZip combines two sequences into a sequence of pairs. The resulting sequence stops when either input sequence is exhausted.

Marble Diagram:

SeqA:   --1--2--3---->
SeqB:   --a--b------->
Zip
Output: --(1,a)-(2,b)|
        (stops when shorter sequence ends)

RxJS Equivalent: [zip] - https://rxjs.dev/api/operators/zip

Example:

seqA := From(1, 2, 3)
seqB := From("a", "b")
result := MonadZip(seqA, seqB)
// yields: (1, "a"), (2, "b")

func Of2

func Of2[K, A any](k K, a A) Seq2[K, A]

Of2 creates a key-value sequence containing a single key-value pair.

Example:

seq := Of2("key", 100)
// yields: ("key", 100)

type Void added in v2.1.2

type Void = function.Void

Void represents the absence of a value, similar to void in other languages. It's used in functions that perform side effects but don't return meaningful values.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL