Skip to content
catena
v1.1.0
Theme

This content is for v1.1.0. Switch to the latest version for up-to-date documentation.

Basics

Basics: building a pipeline, what laziness means in practice, and how catena sequences interoperate with plain range loops and iter.Seq.

Run it: go run ./examples/01-basics

// Basics: building a pipeline, what laziness means in practice, and how
// catena sequences interoperate with plain range loops and iter.Seq.
package main
import (
"fmt"
"slices"
"github.com/NerdMeNot/catena"
)
func main() {
// A pipeline is built stage by stage and runs only when consumed.
squares := catena.Range(1, 100, 1).
Filter(func(n int) bool { return n%3 == 0 }).
Map(func(n int) int { return n * n }).
Take(4).
Collect()
fmt.Println("first four squares of multiples of 3:", squares)
// Laziness is observable: this counts how many elements the source
// actually produced. Take(3) means three — not a hundred.
produced := 0
counted := catena.From(func(yield func(int) bool) {
for i := 1; i <= 100; i++ {
produced++
if !yield(i) {
return
}
}
})
firstThree := counted.Take(3).Collect()
fmt.Printf("took %v, source produced %d elements\n", firstThree, produced)
// Seq IS iter.Seq: range over it directly, no conversion ceremony.
total := 0
for v := range catena.Of(10, 20, 30).Filter(func(v int) bool { return v > 10 }) {
total += v
}
fmt.Println("ranged total:", total)
// And the stdlib's iterators adapt for free in both directions.
fromStdlib := catena.From(slices.Values([]string{"a", "b", "c"}))
backToStdlib := slices.Collect(fromStdlib.Map(func(s string) string { return s + "!" }).Seq())
fmt.Println("round trip through the stdlib:", backToStdlib)
// Chains restructure without rewriting: each stage is a line.
report := catena.Of("chain", "of", "sequences", "linked", "as", "one").
FilterNot(func(w string) bool { return len(w) < 3 }).
MapIndexed(func(i int, w string) string { return fmt.Sprintf("%d:%s", i, w) }).
JoinToString(" ", catena.Self[string])
fmt.Println("indexed words:", report)
// Eager vs lazy, made visible. The same two stages run in a
// different ORDER depending on the type. Lazy (Seq) interleaves:
// each element flows through both stages before the next element
// starts, and no intermediate collection ever exists.
fmt.Println("\nlazy evaluation order (per element, interleaved):")
catena.Of(1, 2, 3).
OnEach(func(v int) { fmt.Printf(" stage A sees %d\n", v) }).
OnEach(func(v int) { fmt.Printf(" stage B sees %d\n", v) }).
Drain()
// Eager (List) stages: stage A finishes the WHOLE collection —
// allocating it — before stage B starts.
fmt.Println("eager evaluation order (per stage, staged):")
catena.List[int]{1, 2, 3}.
OnEach(func(v int) { fmt.Printf(" stage A sees %d\n", v) }).
OnEach(func(v int) { fmt.Printf(" stage B sees %d\n", v) }).
Drain()
// Same elements, same results, different shape of execution — which
// is the whole eager/lazy distinction. When to prefer which:
// docs/02-concepts.md, "Eager vs lazy — the actual difference".
}

View on GitHub