Documentation
¶
Overview ¶
Package stream provides Java Streams-like functional operations on Go collections.
It enables true lazy evaluation, parallel processing, and functional-style pipelines using Go generics and the iter package (requires Go 1.26+, per go.mod).
Intermediate operations compose iter.Seq[T] closures without executing any work. Processing is deferred until a terminal operation ranges over the pipeline. Short-circuit operations (First, AnyMatch, Limit) naturally stop early.
Quick Start ¶
Create a stream from a slice, apply intermediate operations, and collect results:
sum := stream.SliceOf(1, 2, 3, 4, 5).
Filter(func(n int) bool { return n%2 == 1 }).
Map(func(n int) int { return n * n }).
Reduce(func(a, b int) int { return a + b })
// sum == 35
Stream Creation ¶
Use factory functions to create streams:
- SliceOf: from a slice or variadic elements
- From: from an iter.Seq[T] (supports infinite streams)
- From2: from an iter.Seq2[K, V]
- Repeat: infinite repeating element
- RepeatN: element repeated N times
- Concat: combine multiple streams
Operations ¶
Intermediate (lazy, return a new stream):
- Stateless: Filter, Map, Convert (deprecated: use generic MapTo), Peek, FlatMap
- Stateful: Distinct (or generic DistinctBy), Sort, ReverseSort, Reverse, Limit, Skip, Pick
Terminal (eager, execute the pipeline):
- Collect: ToSlice, Collect
- Iterate: ForEach, Seq (native iter.Seq[T] for range loops)
- Reduce: Reduce, ReduceFrom, ReduceWith, ReduceBy
- Match: AllMatch, NonMatch, AnyMatch
- Element: First, Take, Any, Last
- Count: Count
iter.Seq Integration ¶
The Seq() method returns the underlying iter.Seq[T] for use with Go's range:
for v := range stream.SliceOf(1, 2, 3).Seq() {
fmt.Println(v)
}
Parallel Processing ¶
Parallel(n) opens a section of stateless operations that run fused on one worker pool; consecutive Filter/Map/Peek inside the section compose into a single stage. Sections close at stateful operations, type changes, every terminal, and the next Parallel call (which opens a new section — size concurrency per cost profile while sections overlap). Output is unordered unless Ordered() follows:
stream.SliceOf(data...).Parallel(4).Ordered().
Filter(f).Map(g).ToSlice() // equals serial execution element-for-element
See docs/proposals/parallel-v2.md for semantics and measured overheads.
Helper Functions ¶
- To[T, R]: converts a slice of T to a slice of R via a converter
- AnyTo[T]: converts []any to []T via type assertion
Important ¶
Streams are single-use. Each terminal operation consumes the underlying iterator. Create a new stream for each pipeline.
Infinite sources (Repeat, unbounded From) hang non-short-circuiting terminal operations such as ToSlice, Reduce, Count, Last, or Take without a cancellable context — bound them with Limit or WithContext.
Index ¶
- func AnyTo[T any](data ...any) types.Collector[any]
- func To[T, R any](converter types.Converter[T, R]) types.Collector[T]
- type Streamer
- func Concat[T any](srcs ...Streamer[T]) Streamer[T]
- func DistinctBy[T any, K comparable](s Streamer[T], key func(T) K) Streamer[T]
- func From[T any](seq iter.Seq[T], sizeHint int64) Streamer[T]
- func From2[K, V any](seq iter.Seq2[K, V]) Streamer[V]
- func MapTo[T, R any](s Streamer[T], m types.Converter[T, R]) Streamer[R]
- func Repeat[T any](t T) Streamer[T]
- func RepeatN[T any](t T, count int64) Streamer[T]
- func SliceOf[T any](slice ...T) Streamer[T]
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Streamer ¶
type Streamer[T any] interface { // WithContext sets the context consulted by later operations; a // cancelled context makes intermediate operations stop pulling and // terminals return promptly. Applies to the returned stream only. WithContext(context.Context) Streamer[T] // Filter keeps elements for which judge returns true. Lazy, order // preserving; sizeHint becomes unknown (-1). Filter(types.Judge[T]) Streamer[T] // Map transforms each element with a same-type function m. // For a different result type use the generic stream.MapTo. Map(types.Mapper[T]) Streamer[T] // Convert transforms elements to any. Deprecated: it loses the element // type and forces type assertions downstream; use the generic // stream.MapTo[T, R] instead. Kept for backward compatibility. // // Deprecated: use MapTo. Convert(types.Converter[T, any]) Streamer[any] // Peek applies consumer to each element as it passes through without // changing them; useful for debugging or side effects mid-pipeline. Peek(types.Consumer[T]) Streamer[T] // FlatMap flattens each element to a sub-stream and concatenates // sub-streams' elements. The result is Streamer[any]; use AnyTo or To // to recover concrete types. sizeHint becomes unknown. FlatMap(func(T) Streamer[any]) Streamer[any] // Distinct removes duplicate elements keeping first occurrences. Keys // come from fmt.Sprint (or types.Unique), so 1 and "1" collide — // prefer the generic stream.DistinctBy for exact comparable keys. Distinct() Streamer[T] // Sort orders elements ascending by comparator (slices.SortFunc). // Materializes the pipeline stage when iterated; sizeHint preserved. Sort(types.Comparator[T]) Streamer[T] // ReverseSort orders elements descending by comparator. ReverseSort(types.Comparator[T]) Streamer[T] // Reverse yields elements in reverse order; empty input stays empty. Reverse() Streamer[T] // Limit keeps at most the first n elements (n <= 0 yields empty). // Short-circuits the upstream pipeline. Limit(int64) Streamer[T] // Skip discards the first n elements (n <= 0 keeps everything). Skip(int64) Streamer[T] // Pick selects elements at absolute indices start, start+interval, ... // up to end inclusive; end < 0 means the last index (materializing the // stage when sizeHint is unknown); start < 0 or interval <= 0 yields // empty. Pick(startIndex, endIndex, interval int) Streamer[T] // Append yields the stream's elements followed by data. sizeHint grows // by len(data) when known. Append(...T) Streamer[T] // Execute eagerly materializes the pipeline so far and returns a // re-iterable snapshot stream; ctx and parallelSize carry over. Execute() Streamer[T] // Parallel sets worker-pool concurrency for a section of stateless // operations: n <= 0 keeps synchronous execution, n >= 1 runs n // workers on the section's fused stages (Filter/Map/Peek chain into // one pool). A mid-chain call closes the current section and opens a // new one; stateful ops and type changes close sections too. Order is // not preserved unless Ordered() follows. See // docs/proposals/parallel-v2.md. Parallel(int) Streamer[T] // Ordered marks the current (or next) parallel section as // order-preserving: elements are index-tagged and re-sequenced at the // consumer, so output matches serial execution order. No-op in serial // mode. Costs one index stamp and slot lookup per element. Ordered() Streamer[T] // ToSlice collects all elements into a new slice; hangs on infinite // sources unless bounded or cancellable. ToSlice() []T // Collect drains the stream into the caller-provided collector and // returns its result (type any — assert it back). Collect(types.Collector[T]) any // ForEach applies consumer to every element in order. ForEach(types.Consumer[T]) // AllMatch reports whether judge holds for every element; false on the // first violation (short-circuit). AllMatch(types.Judge[T]) bool // NonMatch reports whether judge holds for no element; false on the // first match (short-circuit). NonMatch(types.Judge[T]) bool // AnyMatch reports whether judge holds for at least one element; true // on the first match (short-circuit). AnyMatch(types.Judge[T]) bool // Reduce folds elements with accumulator, starting from T's zero // value; empty input returns the zero value. Reduce(accumulator types.BinaryOperator[T]) T // ReduceFrom folds elements starting from initValue. ReduceFrom(initValue T, accumulator types.BinaryOperator[T]) T // ReduceWith folds elements into an any-typed accumulator, enabling // cross-type reduction; assert the result back to the concrete type. ReduceWith(initValue any, accumulator types.Accumulator[T, any]) any // ReduceBy builds its initial value from the stream's sizeHint (which // may be negative = unknown, e.g. for capacity preallocation), then // folds like ReduceWith. ReduceBy(initValueBuilder func(sizeMayNegative int) any, accumulator types.Accumulator[T, any]) any // First returns the first element or T's zero value when empty; // short-circuits the pipeline. First() T // Take returns a uniformly random element via reservoir sampling — // O(1) memory, honors cancellation; zero value when empty. Take() T // Any is an alias for Take. Any() T // Last returns the final element or T's zero value; consumes the whole // stream and hangs on infinite sources. Last() T // Count returns the element count in O(1) when sizeHint is known, // otherwise by full iteration. Count() int64 // Seq returns the underlying iter.Seq[T] for native range loops. Seq() iter.Seq[T] }
Streamer is a lazily-evaluated pipeline over elements of type T, in the spirit of Java Streams. Intermediate operations compose iter.Seq[T] closures and do no work until a terminal operation iterates; short-circuit terminals (First, AnyMatch, Limit-fed pipelines) stop early.
Streams are single-use: a terminal operation consumes the underlying sequence. Create a new stream for each pipeline.
Usage pattern ¶
Create (SliceOf, From, Repeat, Concat...), transform (Filter, Map...), terminate (ToSlice, Reduce, ForEach...):
sum := stream.SliceOf(1, 2, 3, 4, 5).
Filter(func(n int) bool { return n%2 == 1 }).
Map(func(n int) int { return n * n }).
Reduce(func(a, b int) int { return a + b })
func Concat ¶
Concat concatenates srcs in order: all elements of the first stream, then the second, and so on. sizeHint is unknown (-1) after concatenation. An empty argument list yields an empty stream; short-circuiting the result stops pulling the remaining sources.
func DistinctBy ¶ added in v0.2.0
func DistinctBy[T any, K comparable](s Streamer[T], key func(T) K) Streamer[T]
DistinctBy removes elements whose key, produced by key, has already been seen, keeping first occurrences. Keys use Go map equality (K comparable), avoiding the string-coercion collisions of Distinct. Like Distinct it runs serially — the shared key map is not concurrency-safe — while preserving parallelSize for downstream operations.
func From ¶ added in v0.1.0
From wraps an existing iter.Seq[T] as a Streamer. sizeHint declares the known element count when non-negative (-1 for unknown or infinite); it feeds Count's O(1) fast path and capacity preallocation, so keep it honest. The seq is consumed once — streams are single-use. Infinite sequences are supported but must be bounded (Limit) or made cancellable (WithContext) before a non-short-circuiting terminal operation.
func From2 ¶ added in v0.1.0
From2 adapts an iter.Seq2[K, V] (e.g. maps.All) into a Streamer of the values only; keys are discarded. sizeHint is unknown (-1). Map iteration order is unspecified, so the resulting stream order varies.
func MapTo ¶ added in v0.2.0
MapTo transforms each element of s from T to R, preserving ctx, sizeHint and parallelSize. Unlike Convert it keeps the result type, so no Streamer[any] round-trip with type assertions is needed:
names := stream.MapTo(stream.SliceOf(1, 2, 3), func(n int) string {
return fmt.Sprintf("#%d", n)
}).ToSlice()
As a package function it interrupts method chaining at the type-changing point; Convert chains fluently but erases the element type. Prefer MapTo, especially for head-of-pipeline type changes where chaining resumes right below it; Convert stays acceptable for mid-chain changes despite being deprecated.