记录:Zip/Zip3/Zip4 为何暂不可由自由函数改为方法(跟踪上游 golang/go#80172)
@smallnest is already working on this.
Since Jun 28, 2026.
- Dominant language
- Go
- Stars
- 66
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
背景
Zip、Zip3、Zip4 当前是自由函数(multiseq.go),而非 Seq[T] 上的方法。本文档记录其为何不能直接改成 func (s Seq[L]) Zip[R](b Seq[R]) Seq[Pair[L,R]] 这样的方法形式 —— 便于日后 Go 泛型限制演进时回看。
现状
期望的方法签名:
func (s Seq[L]) Zip[R any](b Seq[R]) Seq[Pair[L, R]]
在 go1.27rc1 上编译失败:
seq.go:39:10: instantiation cycle:
T instantiated as Pair[T, R]
Zip3/Zip4 同理(T instantiated as Tuple3[…] / Tuple4[…])。已在本地实测确认(go1.27rc1 darwin/arm64)。
为什么不可改
核心原因:方法返回类型把接收者自身的定义泛型类型Seq 用一个派生自接收者类型参数 T、但又不嵌套 Seq 自身的实参 Pair[T,R] 重新实例化,触发了 Go 编译器的单态化(monomorphization)循环熔断。
机制(与上游 golang/go#80109 的论证一致):Go 对每个具体实例化生成全部方法。若允许 Seq[int] 的方法返回 Seq[Pair[int,R]],编译器就要为 Seq[Pair[int,R]] 生成全部方法,其签名又含更深的实例化……无穷展开、编译永不终止。instantiation cycle: T instantiated as Pair[T, R] 是熔断器。
这与三条 Go 限制的关系(容易混淆,逐一区分)
| 限制 | 形态 | 状态 | 与本 issue 关系 |
|---|---|---|---|
| golang/go#75883(Go 1.26 放开) | 约束自引用 type T[P T[P]] |
已放开 | 无关。Seq 的 T 约束是 any,没碰约束自引用。不要误以为「1.26 放开自引用了 Zip 就能用方法」。 |
| golang/go#80109(已关闭) | 自嵌套返回 A[A[T]] / Seq[Seq[T]] |
working as intended | 同源机制的另一子类。Chunk/Window 的 Seq[Seq[T]] 属此,已被 Griesemer 判 intended,预期不放开。 |
| golang/go#80172(OPEN) | 派生非自嵌套返回 A[[]T] / Seq[Pair[T,R]] |
未定论,无 Go 团队裁决 | 正是本 issue 所述情形。 |
关于 golang/go#80172
上游 issue golang/go#80172 —— cmd/compile, go/types: generic method returning receiver type instantiated with derived receiver type parameter reports instantiation cycle(2026-06-26 提,作者 JasonMing,截至本文 OPEN,无 Go 团队回复)—— 精准描述了本 issue 的情形。其最小复现:
type A[T any] struct{}
func (a A[T]) M() A[[]T] { panic("TODO") } // Bad — instantiation cycle
func F[T any](a A[T]) A[[]T] { panic("TODO") } // OK — 同形状的自由函数可编译
A[[]T] 与本库的 Seq[Pair[L,R]] 同构:都是「把接收者类型用派生自 T、但不嵌套接收者自身的实参重新实例化」。作者的论点很关键 —— []T(以及本库的 Pair[T,R])并不递归展开 A/Seq,因此 #80109 那套「自嵌套无穷展开」的论证套不上这一子类;且等价的自由函数能编译,说明返回类型本身有限且静态可知。作者请求 Go 团队「要么让它编译,要么文档化为何不行」—— 目前无裁决。
也就是说:本 issue 描述的限制当前确实生效,但它是否「按设计如此」尚未被 Go 团队定论(与 #80109 自嵌套类不同)。
当前 workaround(已采用)
不改成方法,保持自由函数形式。返回类型用另一个定义类型 Seq2[L,R](或底层 iter.Seq[Pair[L,R]])即可绕开 —— 实测可编译且惰性可链:
// 已验证可编译 + 惰性可链(非终止):
func (s Seq[L]) ZipM[R any](b Seq[R]) Seq2[L, R] // 返回 Seq2,而非 Seq[Pair[L,R]]
ZipWith(返回 Seq[C],C 为全新参数)与 Unzip(Seq2 上的方法,返回 Seq[K]/Seq[V])均可为方法,本库取自由函数纯为多源对称。
验收 / 跟踪
- 现状已记录:
Zip/Zip3/Zip4为自由函数,go1.27rc1 实测方法形式报instantiation cycle - 机制与上游 issue 的对应关系已厘清(#75883 / #80109 / #80172)
- 跟踪 golang/go#80172:若 Go 团队裁定该子类「应放开」并修复,则本库可将
Zip/Zip3/Zip4改为方法(返回Seq[Pair[L,R]]/Seq[Tuple3[…]]/Seq[Tuple4[…]]),并更新multiseq.go注释、docs/API.md3.3 表、docs/en.html/zh.html的 multi-section desc - 若 Go 团队裁定该子类 intended(同 #80109),则本 issue 关闭、现状定型
Type
docs / tracking · 优先级 low · 阻塞于上游 golang/go#80172
相关
- 代码:
multiseq.go文件头注释、docs/API.md§3.3、docs/en.html/zh.htmlmulti-section - 上游:golang/go#80172(本情形)、golang/go#80109(自嵌套同源)、golang/go#75883(约束自引用,无关)
- 本库同源现象:
Chunk/Window返回iter.Seq[Seq[T]](属 #80109 自嵌套类)
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.