smallnest / smallnest/seq

记录:为何使用约束子类型 SeqComparable/SeqOrdered/SeqNumeric(模拟上游 #65394 refined method constraints)

Open
#45 0 comments 0 reactions 1 assignee View on GitHub

@smallnest is already working on this.

Since Jun 28, 2026.

docs priority: low
Dominant language
Go
Stars
66
Forks
1
PR merge metrics
No merged PRs in 30d

Description

背景

本库用三个「约束子类型」SeqComparable[T comparable] / SeqOrdered[T cmp.Ordered] / SeqNumeric[T Numeric](见 subtypes.go)来恢复受约束操作(Distinct/Max/Sum/...)的左到右链式调用:

seq.Numbers(seq.From([]int{1,2,3})).Distinct().Sum()

本文档记录为何要引入这套子类型机制 —— 它本质上是对上游提案 golang/go#65394(及其评论里 Merovius 称之为 "refined method constraints" 的特性)的手动模拟。一旦该提案被接受,本库可大幅删减 subtypes.go 的样板代码。

问题:Seq[T any] 上无法挂受约束方法

Seq[T]iter.Seq[T] 的 defined type,T 在类型层面声明为 any。任何要求 T 本身满足约束(comparable / cmp.Ordered / Numeric)的操作 —— Distinct(要 ==)、Max/Sort(要 <)、Sum/Product(要 +/*)—— 都不能作为 Seq[T any] 的方法,因为方法签名会在不满足约束的 T 上被实例化。这正是本库「划分铁律」的一条:约束 T 本身的操作只能是自由函数。

代价:链式调用被打断,必须退回自由函数形式:

// 受约束操作是自由函数,读起来不再左到右:
seq.Sum(seq.Distinct(seq.From([]int{1,2,3})))

上游提案 #65394:refined method constraints

golang/go#65394 —— proposal: spec: allow methods on generic structs which meet subset of type constraint(OPEN,milestone Proposal,带 LanguageChangeReview 标签,截至 2026-01 仍 pending,未接受/未拒绝)—— 提议允许给方法额外施加一个比类型参数更窄的约束

type X[T C2] struct{}
func (X[T C1]) M() {}   // 仅当 T 同时满足 C1(C1 是 C2 的子约束)时,M 才进入 X[T] 的方法集

其中 golang/go#65394#issuecomment-1933627587(Merovius, 2024-02-08)是这条提案下最系统的论述,把该特性命名为 "refined method constraints"(精炼方法约束),并专门用一节 「Restricting to sub constraints」 讨论「方法约束必须是类型约束的子约束」这一限制 —— 与本库「子约束类型」的命名直接对应。该评论的核心要点:

  1. 可行性有据:该机制已在 Featherweight Generic Go 论文中被证明类型安全(虽非真实 Go 的完整证明,但提供了可行证据)。
  2. 可行性落地:编译器本就要为不同类型实参生成方法的不同实现(Add 对 int/float 不同);refined method constraints 不过是在实例化时,移除那些类型实参不满足的「精炼方法」,复用现有单态化启发式即可 —— 不需要运行时代码生成。
  3. 子约束检查不比现状难:验证「C1C2 的子约束」与现有的「带类型参数的泛型调用 G[T]() 是否合法」是同一个检查(F[T C2] 内调用 G[T C1] 仅当 C2 ⇒ C1)。
  4. 解决表达式问题 / 可选接口:除泛型特化外,还顺带解决 Go 长期存在的「optional interfaces」问题(如 http.ResponseWriterFlusher/Hijacker/Pusher 组合)—— 用线性代码替代 2ⁿ 个组合类型。

本库的子类型机制 = #65394 的手动模拟

#65394 若被接受,理想写法是直接在 Seq[T] 上声明受约束方法:

// 假想:#65394 落地后
func (s Seq[T comparable]) Distinct() Seq[T] { ... }
func (s Seq[T cmp.Ordered]) Max() Optional[T] { ... }
func (s Seq[T Numeric]) Sum() T { ... }
// 于是直接:
seq.From([]int{1,2,3}).Distinct().Sum()

但在该提案落地前(且 Go 1.27 仍未含此特性),本库用手动造子类型来模拟:把约束"焊死"在类型参数上,造出 SeqComparable/SeqOrdered/SeqNumeric 三个并行 defined type,受约束操作成为各自的方法。进入子类型用自由函数(Numbers/Ordered/Comparable,约束 T),降级用方法(强约束已满足弱约束:Numeric ⊂ Ordered ⊂ comparable)。

代价subtypes.go 的大量样板正是这套模拟的副产物 —— 每个 T-保持的中间方法(Filter/Reject/Take/Drop/TakeWhile/DropWhile/Peek,共 7 个)要在三个子类型上各重写一遍以保持链式类型,外加受约束操作在更强子类型上的继承重写。subtypes.go 因此膨胀到 400+ 行,且每新增一个 T-保持中间方法就要同步改三处。这正是 #65394 所要消除的样板。

验收 / 跟踪

  • 现状已记录:受约束操作因 T: any 不能是 Seq[T] 方法,故用 SeqComparable/SeqOrdered/SeqNumeric 子类型模拟
  • 与上游提案的对应关系已厘清:本机制 = #65394 refined method constraints 的手动模拟
  • 跟踪 golang/go#65394:若被接受并落地(Go 1.x),则本库可:
    • 删除 SeqComparable/SeqOrdered/SeqNumeric 三个子类型及其全部样板;
    • Distinct/Max/Min/Sort/Sum/Product/Mean/Contains/IndexOf/... 改为 Seq[T comparable] / Seq[T cmp.Ordered] / Seq[T Numeric] 上的 refined method;
    • 入口自由函数 Numbers/Ordered/Comparable 与降级方法随之移除;
    • 同步更新 subtypes.go(很可能整体删除)、docs/API.mddocs/en.html/zh.html
  • 若 #65394 被拒绝,则本机制定型为长期方案,本 issue 关闭

Type

docs / tracking · 优先级 low · 关联上游 golang/go#65394

相关

  • 代码:subtypes.go(子类型定义与方法)、seq.goSeq[T any] 与划分铁律)、comparable.go/numeric.go(受约束自由函数)
  • 文档:docs/API.md 划分铁律、docs/en.html/zh.html constrained 段
  • 上游:golang/go#65394(本机制对应的提案)、#65394 的 Merovius 评论(refined method constraints 系统论述,含「Restricting to sub constraints」节)
  • 同类跟踪 issue:#44(Zip/Zip3/Zip4 实例化循环,跟踪 golang/go#80172)—— 两者都是「用设计手段绕开 Go 泛型当前限制」的记录

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.