Finally, Go Gets Scala‑Style Chainable Pipelines with the seq Library

The seq library introduces left‑to‑right chainable lazy collections for Go, letting developers write pipelines that read like Scala, explains why Go waited for generic methods until version 1.27, showcases concrete examples, design decisions, limitations, and how to get started.

BirdNest Tech Talk
BirdNest Tech Talk
BirdNest Tech Talk
Finally, Go Gets Scala‑Style Chainable Pipelines with the seq Library

Overview

seq is a Go library that provides left‑to‑right chainable collection pipelines using Go 1.27 generic methods. It builds on the standard library iterator type iter.Seq[T], allowing zero‑cost conversion to and from seq.Seq[T].

Motivation

Prior to Go 1.27 methods could not declare their own type parameters, so libraries such as samber/lo exposed all operations as top‑level functions. This forced nested calls like lo.Sum(lo.Map(lo.Filter(...))), where the data flow is “inside‑out” but the reading order is “outside‑in”.

The generic‑method proposal golang/go#77273 landed in Go 1.27, removing the limitation and enabling true method‑chain syntax, which seq exploits.

Basic usage

sum := seq.From([]int{1,2,3,4,5,6}).
    Filter(func(x int) bool { return x%2 == 0 }).
    SumBy(func(x int) int { return x * x })

In contrast, the same operation with lo requires nested calls:

sum := lo.Sum(
    lo.Map(
        lo.Filter([]int{1,2,3,4,5,6},
            func(x int, _ int) bool { return x%2 == 0 }),
        func(x int, _ int) int { return x * x },
    ),
)

Design highlights

Core type : type Seq[T any] iter.Seq[T] (essentially func(yield func(T) bool)). This enables zero‑cost conversion between iter.Seq and seq.Seq, and seamless use with slices.Collect or maps.Keys.

Free generic functions vs methods : An operation that adds a constraint on the element type T (e.g., Distinct, Max, Sum) is implemented as a top‑level generic function; operations that only need the receiver’s type parameter (e.g., GroupBy[K], Map[U]) remain methods. The rule is:

If the operation adds a constraint on T , use a free generic function; otherwise use a method.

Sub‑type entry points : Types such as Numbers, Ordered, and Comparable embed the appropriate constraints, allowing constrained operations to be called as methods after the entry point (e.g., seq.Numbers(...).Distinct().Sum()).

Examples from the test suite

Grouping, distinct, sum

groups := seq.From([]int{1,2,3,4,5,6}).
    GroupBy(func(x int) string {
        if x%2 == 0 { return "even" }
        return "odd"
    })
// groups => map[string][]int{"even": {2,4,6}, "odd": {1,3,5}}

total := seq.Numbers(seq.From([]int{1,2,2,3,3,3})).
    Distinct().
    Sum()
// total => 6  // 1+2+3

Lazy infinite sequence

powers := seq.Iterate(1, func(x int) int { return x * 2 }).
    Take(5).
    Collect()
// powers => []int{1,2,4,8,16}

Short‑circuiting operations such as Find, Any, and All stop evaluation as soon as the result is known.

Prefix sum (scan)

prefix := seq.From([]int{1,2,3,4}).
    Scan(0, func(acc, x int) int { return acc + x }).
    Collect()
// prefix => []int{0,1,3,6,10}

Sliding window

wins := seq.From([]int{1,2,3,4,5}).Window(3, 1)
// wins => [][]int{{1,2,3},{2,3,4},{3,4,5}}

Zip and ZipMap

m := seq.ZipMap(
    seq.From([]string{"a","b","c"}),
    seq.From([]int{1,2,3}),
)
// m => map[string]int{"a":1,"b":2,"c":3}

Limitations

No built‑in error‑handling chain; a separate proposal is planned.

Only sequential lazy pipelines; parallel execution is not provided.

All operations are read‑only; the original slice is never mutated.

Flatten supports only one level of nesting because deeper flattening cannot be expressed in Go’s type system.

Tuple support stops at Tuple4; larger tuples must be defined manually.

Operations that require materialising the whole sequence (sorting, reversing, taking the last n elements, sliding windows) are implemented in intermediate_materializing.go and are marked as “internal materializing”.

The gofmt bug that mis‑reports generic method signatures in go1.27rc1 disappears in the final release.

Getting started

go get github.com/smallnest/seq

Requires Go 1.27 (or later). The repository, design documents, and full API signatures are available at https://github.com/smallnest/seq.

Conclusion

With Go 1.27’s generic methods, seq demonstrates that collection pipelines can be expressed in a clean, left‑to‑right fashion, avoiding the nested‑call style that was unavoidable before.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

golangGogenericsfunctional programminglazy evaluationseq librarycollection pipelines
BirdNest Tech Talk
Written by

BirdNest Tech Talk

Author of the rpcx microservice framework, original book author, and chair of Baidu's Go CMC committee.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.