Go泛型浅尝

https://go.dev/blog/why-generics

关于泛型


Java是伪泛型,因为运行时会擦除掉..

关于运行时多态


泛型和其他特性一样不是只有好处,为编程语言加入泛型会遇到需要权衡的两难问题:语言设计者需在编程效率、编译速度和运行速度三者进行权衡,编程语言要选择牺牲一个而保留另外两个


Go 1.18以前


为什么 Go 语言没有泛型


Go 1.18


该部分内容是对Go 1.18 泛型全面讲解:一篇讲清泛型的全部的学习与记录

如果你经常要分别为不同的类型写完全相同逻辑的代码,那么使用泛型将是最合适的选择

泛型能实现的功能,通过接口+反射也基本能实现。但反射机制有很多问题:

  • 用起来麻烦,要写一大堆 例如 switch case int时如何,int64时如何…
  • 失去了编译时的类型检查,不仔细写容易出错。有问题要在运行时才能发现
  • 性能不理想

一般用法


例如判断切片中是否包含某个元素的方法,不同类型的元素需要完全再写一遍…

或者使用interface,但每次传递时,都需要将原切片转成interface类型的切片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"fmt"
)

func main() {
sli := []int64{1, 2, 3}
var sliIface []interface{}

for _, item := range sli {
sliIface = append(sliIface, item)
}

rs := InSliceIface(int64(2), sliIface) // 2 必须指定为int64类型,否则会当成int,最终结果为false
fmt.Println(rs)
}

func InSliceIface(ele interface{}, sli []interface{}) bool {
for _, v := range sli {
if v == ele {
return true
}
}
return false
}

在此使用泛型,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
)

func main() {
sli := []float64{1, 2, 3.14}

rs := InSlice(3.14, sli)
fmt.Println(rs)
}

func InSlice[T int | int8 | int32 | int64 | float32 | float64 | string](ele T, sli []T) bool {
for _, v := range sli {
if v == ele {
return true
}
}
return false
}

输出:

true

可见,方便简洁了很多


图片来自Go 1.18 泛型全面讲解:一篇讲清泛型的全部,版权归原作者所有




高阶用法 & 注意事项

匿名结构体不支持泛型,下面的用法是错误的:

1
2
3
4
5
6
7
8
9
testCase := struct[T int|string] {
caseName string
got T
want T
}[int]{
caseName: "test OK",
got: 100,
want: 100,
}

所以在使用泛型时 只能放弃使用匿名结构体,对很多场景有影响(最主要的是写单元测试时)


通过泛型receiver,泛型的实用性一下子得到了巨大的扩展。在没有泛型之前如果想实现通用的数据结构,诸如:堆、栈、队列、链表之类的话,选择只有两个:

  • 为每种类型写一个实现
  • 使用 接口+反射

而有了泛型之后,就能非常简单地创建通用数据结构


匿名函数不支持泛型


函数支持泛型,但方法目前还不支持泛型。但因为receiver支持泛型, 所以如果想在方法中使用泛型的话,目前唯一的办法就是曲线救国,迂回地通过receiver使用类型形参

Go泛型介绍[译]


2024.02.29

给Go提了一个新的函数:

https://go-review.googlesource.com/c/go/+/568095


涛叔这篇不错~
taoshu-Go语言泛型设计

Go泛型真的要来了!最早在Go 1.17版本支持



20241003

Mat、Johnny、Jon 和特邀嘉宾 Ian Lance Taylor 讨论了 Go 中的泛型。什么是泛型?为什么它们有用?为什么接口不够用?如果将泛型添加到 Go 中,标准库将如何变化?社区对泛型有何贡献?如果添加泛型,这将对语言产生什么负面影响?

本篇内容是根据2019年8月份#98 Generics in Go音频录制内容的整理与翻译

过程中为符合中文惯用表达有适当删改, 版权归原作者所有.


Mat Ryer: 大家好,欢迎来到 Go Time!我是 Mat Ryer,今天我们要讨论的是“泛型”,这是一个既有趣又有时颇具争议的话题。今天和我一起的有 Jon Calhoun……你好,Jon。

Jon Calhoun: 嗨,Mat!

Mat Ryer: 还有 Johnny Boursiquot……

Johnny Boursiquot: 大家好。

Mat Ryer: 以及唯一的 Ian Lance Taylor。你好,Ian。

Ian Lance Taylor: 你好!谢谢你们邀请我。

Mat Ryer: 是啊,谢谢你加入我们。这太让人兴奋了。我们都在 GopherCon 上看到你演讲,实际上今天视频已经发布了。所以如果有人还没看过 Ian 在 GopherCon 上关于这个话题的演讲,现在你可以去看 这个视频

Jon Calhoun: 不是现在……大概几个小时后。

Mat Ryer: 说得好,谢谢 Jon。 [笑] 或许我们可以先从泛型是什么,及其对 Go 的意义开始聊聊。为什么这是一个我们不断讨论、不断听到的话题,尤其是那些从其他编程语言看 Go 的人。

谁想先来解释一下泛型,供不太熟悉这个概念的听众了解?

Ian Lance Taylor: 我很乐意试一试。泛型是一种编程方式,你在编写代码时不指定值的确切类型,而是使用类型参数代替实际类型。然后在实际构建和执行程序时,才选择这些类型。这样你可以编写一套算法和数据结构,它们可以独立于实际类型而工作。

Mat Ryer: 说得很好。但这和空接口不同,对吧?泛型依然会在编译时进行类型检查。

Ian Lance Taylor: 是的,没错。这仍然是在编译时基于类型参数进行静态检查。

Jon Calhoun: 那么你会说,映射(map)和切片(slice)在某种程度上也是泛型的一个例子吗?

Ian Lance Taylor: 是的,映射和切片本质上都是泛型类型。它们是内置在语言中的泛型。所以当人们谈论 Go 中的泛型时,可以说他们想要自己编写类似映射和切片的泛型数据结构和算法,而不是直接使用内置的映射和切片。

Johnny Boursiquot: 对于一些人来说,没有泛型会带来一些痛苦---或者说人们认为这是痛苦的。因为如果没有泛型,你要么依赖空接口并进行类型转换,这本身带来了风险;要么你可以生成代码……但问题是,为什么我们需要泛型?其他编程语言有泛型,Go 则一开始就没有采用这种编程方式。你怎么看待当初为什么没有引入泛型,现在为什么又要重新考虑这个问题?

Ian Lance Taylor: 当时没有引入泛型的原因是它很复杂。你需要非常仔细地考虑如何进行类型检查,如何让程序工作。定义类型参数和类型实参本身也很复杂。

现在,我希望我们通过设计草案将复杂性最小化,但不可否认的是,它会为语言增加一些新概念。不过话说回来,人们希望在 Go 中看到泛型是有原因的---正如你所说,人们熟悉其他语言中的泛型。在 Go 中,有一些代码类型我们无法编写,因为我们没有泛型。虽然没有泛型我们依然可以写出大量优秀的代码,但如果我们有了泛型,我们可以编写一些现在无法实现的库。例如,一个典型的例子是 ConcurrentHashMap,一个可以在多个 goroutine 同时安全修改的哈希映射,并且像标准语言中的映射类型一样是类型安全的。

另一个例子是一些现在无法编写的算法,比如适用于任意类型通道的算法。你可以编写简单的函数,将两个通道合并,或者将一个通道多路复用到多个其他通道中,或者进行其他你想做的事情。目前,你需要为每种类型单独编写,因为你无法表示“我有一个通道,但我不关心通道的类型。”你总是必须说“我有一个 chan int”或“我有一个 chan struct”,而不能只说“我有一个通道,我还想在它上面写一个 select 语句。”这是目前在 Go 中很难实现的。

Jon Calhoun: 我记得---是上周吗,Mat?当时你们在讨论 io.Writer 和 io.Reader 接口……

Mat Ryer: 是的。

Jon Calhoun: 这让我想起了这个。我们可以围绕这些流行的接口编写很多很酷的代码,而且我们并不关心我们从哪里读取……听起来你在说,关于通道,我们也可以做类似的事情。但遗憾的是,我们目前无法做到这一点,尽管我们本可以围绕通道构建出许多通用功能。

Ian Lance Taylor: 是的,没错。所以泛型可以帮助 Go 程序员的一个方式是---正如你所说,你可以编写非常强大的接口,但你必须编写实现这些接口的方法。你可以从概念上看作 Go 中的所有内置类型都有它们自己的方法。它们不是以方法的形式编写的,而是像加号(+)或通道的接收和发送操作符这样的运算符。但目前我们无法将这些概念捕捉到接口中,而泛型可以让我们做到这一点。

但泛型不仅仅是接口。你还可以编写描述多个类型之间关系的泛型。你不必总是处理单一类型。例如,典型的例子是图(graph),其节点和边(node 和 edge)类型是不同的。你可以编写通用的图算法,这些算法适用于实现图算法要求的类型,但你不必指定这些类型究竟是什么。

Mat Ryer: 在这种情况下---我在你的演讲中看到了这个例子,Ian---如果你有一个图和一个节点,并且你有一个同时包含两者的契约(contract),那么这个契约只有在你为它们提供了类型时才有意义,对吗?这些类型基本上是必须的。

Ian Lance Taylor: 是的。在演讲中的图例子中---是的,每次你想要处理一个图时,你都必须提供两个类型参数,一个描述节点类型,一个描述边类型。

Mat Ryer: 这很合理。当然,如果你忘记了其中一个类型,编译器在那时就会帮助你。

Ian Lance Taylor: 是的。这就像你调用一个函数时没有传递足够的参数一样。

Jon Calhoun: 当我们谈论这些不同的数据结构时,我有个问题:你觉得因为泛型的引入,标准库会变得更大吗?

Ian Lance Taylor: 这很难说。我不觉得标准库会变得非常大。我会说,我预计会看到一个新的 chans 包,比如包含我提到的通道算法。同样,也会有一个新的 slices 包,里面包含适用于任何类型切片的一些简单切片算法。除此之外,很难说。

我认为人们将能够编写适用于不同方式的泛型数据结构,但大多数这些数据结构会存在于标准库之外。我认为只有当我们看到这些结构有明确的用例时,才会考虑将它们引入标准库。所以我不认为标准库会一下子变得非常大,但当然,仍然可以根据需要添加一些新的具有普遍适用性的东西。

Jon Calhoun: 这在 Go 2 的讨论中经常出现;虽然泛型可能不需要 Go 2 才能实现,但大家似乎都在这个背景下讨论泛型。我猜其中一个好处是标准库中的某些现有包可能会因为泛型的引入而改变。我想象,像 Sort 包可能会有变化。你觉得这是对的吗?

Ian Lance Taylor: 是的,我同意。Sort 包可能会改变,Container List 包和 Hash 包也会。我们可能会保留旧的包,但很可能会有新的版本使用泛型设施。

Mat Ryer: 但我们不会有新的切片类型,对吧?

Ian Lance Taylor: 不会。

Mat Ryer: 切片类型应该会保持不变,是吧?

Ian Lance Taylor: 是的,切片已经很好了。没有理由改变它们。

Mat Ryer: 它们确实很好。

Jon Calhoun: 我觉得 Sort 包很好用,特别是当你掌握它后。但如果你习惯了其他语言的方式,初次接触 Sort 包时,有时会觉得有点困惑。虽然它已经越来越好,但我确实觉得泛型可以让 Go 变得更易用。

Mat Ryer: 其实,Sort 包展示了如何通过现有 Go 代码实现类似泛型行为的一个很酷的例子。你可以传递一个函数,并依赖闭包访问数据;你只需要比较两个元素的索引。这有点像个技巧,但确实有效。当然,泛型远远超越了这一点。

Ian,你之前说的一点让我深有共鸣,你提到 Go 中没有泛型是因为它很复杂。我觉得对开发人员和工程师来说,这个概念非常合理;但对于从事产品工作的人来说,他们通常不理解这一点……所以听到这个观点很不错。而且我也很高兴看到你们在 Go 团队中能够基于技术现实和“机械同情”(mechanical sympathy)做出这样的决策。

Ian Lance Taylor: 是的……我认为 Go 成功的很大一部分原因在于它的简单性。当你编写程序时,如果你花费几分钟或几个小时来决定应该使用哪种语言结构(在某些其他语言中),那么那不是生产性的时间。你希望语言足够强大,能够完成所有任务,但又不至于太复杂。你不希望纠结于语言的某些方面究竟是如何工作的。

所以如果我们最终在 Go 中添加了泛型,我们必须保留这种特性。这是语言中最重要的特性。

Mat Ryer: 是的,还有可读性。我经常强调这一点---编写代码时更多是为了使用 API 和阅读代码,而不是为了编写本身。这也是为什么我个人不介意总是写 if err != nil。我已经非常习惯了,因为当我阅读代码时(我做得更多的是阅读而不是编写),它非常清晰,表达非常明确。

这是我喜欢最新泛型提案的一点。如果你看代码,它依然看起来像 Go……尽管现在有了额外的一组括号需要考虑。

Ian Lance Taylor: 很高兴听到你这么说,因为这是我们真正追求的目标之一。它应该仍然看起来像 Go。

Mat Ryer: 是的,我觉得这是一个很好的目标。这也是我反对 Try 提案的原因之一。我稍微跑题了……我觉得 Try 提案有点像魔法,不太符合我对 Go 的直观认识。而最新的泛型提案,我觉得它仍然保留了 Go 的风格,如果可以这么说的话……

Ian Lance Taylor: [笑] 很好。

Jon Calhoun: 在你们设计这个功能时,为了让它易读且易用,我猜测你们也参考了其他语言,寻找灵感,看看哪些方式行得通,哪些不行……你能谈谈这个过程吗?

Ian Lance Taylor: 当然。很明显---也许并不那么明显,但事实是我们最熟悉的语言是 C++,所以我们花了大量时间研究 C++ 中的泛型实现,当然,在 C++ 里它被称为模板(templates)。我们很清楚,C++ 模板有些方面很难引入到 Go 语言中,甚至我们根本不想让它们进入 Go。你可以把 C++ 的模板看作是另一种编程语言,实际上它是图灵完备的,像是叠加在普通 C++ 语言上的一层,只不过它使用的是完全不同的语法,并且在编译时执行。

这就是人们所说的模板元编程(template metaprogramming)。你实际上可以用模板语言编写整个程序,但这些程序非常难以理解……但这不是我们想要走的方向。我们希望剔除这些复杂性,只保留核心思想,让人们能够使用类型。

当然,我们也研究了 C++ 的语法,很多人熟悉使用尖括号(angle brackets)的方式,但我们无法找到在 Go 中让它工作的方法……因为在 Go 中,你可以在不知道名字的具体类型的情况下解析语法;虽然为了完全解析程序你需要知道类型,但你实际上可以在不知道类型的情况下完成所有解析,而 C++ 并非如此。在解析 C++ 时,你需要知道某个东西是模板还是普通变量。而我们需要保留 Go 语言中轻松解析的能力,这让编译速度更快,也让很多重要工具,比如 goimports 更容易解析代码,因为它们不需要理解每个名字的类型。

这是我们开始时的情况……当然,我们还参考了很多其他语言,比如 D、Ada、CLU……CLU 早在 70 年代就有了很多泛型的思想。很遗憾,这门语言没有继续发展下去。当然,我们也参考了 Java。

Mat Ryer: 我喜欢的一点是,在某些情况下,作为用户,你可能并不需要知道泛型的存在。比如有些例子中,可以从你传入的参数推断出类型。因此,在这些情况下,它看起来就像在调用一个普通的 Go 函数,这一点我非常喜欢。

Ian Lance Taylor: 没错。类型推导实际上是我们花了大量时间研究的内容。因为一方面,我们非常清楚自己想要它,正如你所说,用户可以调用一个泛型函数,甚至不一定意识到它是个泛型函数。但我们也必须制定不会让人感到意外的类型推导规则,这也是我们从 C++ 中了解到的。C++ 还有函数重载和类型推导,这些非常复杂,有时确实会让人感到意外。因此,我们花了很长时间写下了一套足够简单的规则,适用于大部分情况……至少我们希望它们足够简单,适用于大多数情况。毕竟,迄今为止还没有太多泛型代码被编写出来,因为还没有完整的实现。

Jon Calhoun: 那么,当你们考虑不同的方案时,会写部分实现来试验一下吗?

Ian Lance Taylor: 是的。

Jon Calhoun: 我知道现在(我想)有部分实现,但我不确定你们是否会为每个方案都写部分实现。

Ian Lance Taylor: 是的。我们为许多不同的方案写了部分实现,这确实帮助我们发现了解析问题。我们会在解析器中实现它,并用一些测试用例来试验;或者仅仅在解析器中写代码时,我们就会遇到一些情况,发现“哇,我们完全不知道该如何解析这段代码。”这帮助我们逐步走向今天建议的这种相对简单的语法。

Mat Ryer: 这是一个非常有趣的方式……因为你需要解析这门语言,所以解析成为了一个主要关注点。这并不是我们从外部设计泛型时会想到的事情;我们总觉得“设计什么都可以,任何我们能在记事本中输入的东西都可以。”但实际上,你还得考虑现有工具和解析器等的兼容性。我看到 Contracts 已经在解析器中实现了,我想有个 PR。

Ian Lance Taylor: 没错,Robert Griesemer 已经写了一个解析器,并且完成了大部分类型检查器的工作。这项工作进展得非常顺利。

Mat Ryer: 这真令人兴奋。

Johnny Boursiquot: 除了这个概念被引入语言中,contract 是唯一一个新增的非常显眼的关键词吗?这是开发者会意识到泛型功能的第一步吗?

Ian Lance Taylor: 是的,没错。一个新的关键词---``contract。在当前的设计中,这就是我们唯一新增的内容。你说的对,这是开发者最先看到的东西……但实际上我认为大多数人不会首先使用 contract。虽然 contract 是我们设计中的关键元素,但你实际上可以在没有 contract 的情况下编写很多泛型代码。我认为我们确实需要 contract,但你可以不依赖它,直接使用类型参数和类型实参来写很多代码。

Mat Ryer: 你的意思是使用现有的 contract 吗?内置的那些?

Ian Lance Taylor: 不,我指的是完全不使用 contract 来写代码。比如我提到的通道算法。你可以使用类型为 T 的通道写很多东西。你根本不关心 T 的具体类型,所以不需要 contract

Mat Ryer: 我明白了,任何类型都可以传入。

Ian Lance Taylor: 没错,完全正确。

Mat Ryer: 我觉得,一旦泛型可用,很多经典问题会立即得到解决,尤其是在标准库中解决。接下来我想讨论的是,首先,社区如何为此做出贡献?其次,我很感兴趣的是---如何避免我们每个人都去建立自己的库,如何避免每个人都各自实现一遍通用功能?如何围绕一个中心化的地方来集合这些资源?希望我们需要的所有东西,比如集合、图结构、树等等都能共存在一个地方……你觉得这些会进入标准库,还是会先在社区中出现?

Ian Lance Taylor: 这些问题很棒,但我不知道最终会如何发展。我希望很多东西会先在外部实现,然后再进入标准库。但正如你所说,有些非常明显的东西,比如集合,似乎从一开始就应该添加进去。我现在还不确定情况会怎样发展。

Mat Ryer: 是的,很有趣。比如 sync.Map 是你提到的一个例子,如果能直接有一个带类型的 sync.Map 就很棒了。

Ian Lance Taylor: 没错。

Mat Ryer: 因为当你开始学习 Go 时,通常并发是你非常想要尝试的东西,因为语言原生支持这些并发原语……我知道很多人对 Go 的这部分非常感兴趣。所以如果能以一种直观、简单的方式使用 sync.Map,这无疑会帮助很多新开发者。而且我觉得这个功能大多会吸引那些更高级、更有经验的开发者。初级开发者可能一开始会避开它。我不知道你们怎么看这个问题……

Jon Calhoun: 至少在我看来,我希望这是一个你不需要强制使用的东西。如果你不编写提供泛型实现的库,而是像现在一样使用映射和切片,你甚至不需要考虑它们是泛型的。我希望这种情况能够让初级开发者不会一开始就被吓跑。

Mat Ryer: 是的。我觉得现在编程中常见的一个错误---我自己也犯过这种错误---就是过早抽象化。当我看到一个概念时,我很容易立即想把它抽象出来,但现在我会尽量多实现几次,看看是否真的需要抽象。泛型的强大功能可能会让人们更早地去构建抽象,这很诱人。这是我们作为社区需要讨论的一个问题。说到社区……你们对此有什么想法吗?

Ian Lance Taylor: 我完全同意你说的。新事物确实容易被过度使用。我觉得在 Go 的早期,通道也是这样。我们花了一段时间才真正理解通道的用处,以及它们在哪些情况下带来了过多的复杂性,或者过早的抽象。我们需要尝试学习,并且希望能建立一个良好且简单的基础,以便我们在其基础上继续学习。

Mat Ryer: 是的,完全同意。我是早期滥用通道的那类人。我在很多地方使用它们,实际上根本不需要……现在我通常是从互斥锁(mutex)开始,有时候甚至不需要进一步扩展。但我记得那段时间……我们现在理所当然地认为可以轻松启动 goroutine,让它们安全地通信,并使用这些语言原语实现这一切,这非常强大……所以我可以理解为什么人们会兴奋并想要使用它。

Ian Lance Taylor: 是的。

Mat Ryer: 说到社区,社区在泛型中的贡献有多大?我知道每当你们讨论任何特性或语言改动时,都会引发大量讨论……我认为这也体现了 Go 团队的一些核心价值观,比如简洁性。社区似乎对变化有一定的抵触情绪……你觉得社区的参与度如何?是否达到了预期?我们可以如何改进?你对此有何感受?

Ian Lance Taylor: 我认为社区的表现非常好。多年来,我们从社区中获得了很多想法。实际上,泛型的讨论自 Go 诞生以来就一直持续。很多人贡献了非常有趣且有用的想法……当然,也有人说“不要泛型,无法接受,太复杂了”,我尊重这种观点。当然,我现在讨论的是泛型,但目前还不能保证它们一定会进入语言;我希望它们能进入。

多年来,关于泛型的讨论有很多不同的观点和想法,我认为这些讨论帮助我们了解了如何解决这个问题,以及我们可以做些什么。它还帮助我们看到了那些本可以通过泛型更容易解决的代码实例……从而确保我们提出的设计足够强大,能够解决这些问题。

因此,最有用的东西就是那些泛型能够帮助解决的实例,这些实例确保我们提出的设计确实有用。社区还对语法和语义提出了很多好的想法……虽然有些想法过于复杂,但它们帮助我们在核心功能和强大性之间找到了平衡,这将使泛型成为语言的一个有用补充。

Jon Calhoun: 我记得大概在过去一两年---也许更早一些---我看到有一个 Google 团队的成员举了一个例子,说明使用空接口(empty interface)实际上造成了性能问题,而泛型可以帮助解决。但似乎我们花了很长时间才看到这种真实的例子。这是对的吗,还是我遗漏了什么?

Ian Lance Taylor: 不,你说得很对。这类问题的确需要时间来理解。理解任何语言都需要时间,理解空接口的性能影响也需要时间……所以你说的没错。

Jon Calhoun: 那你认为这是否是我们现在更加关注泛型的原因之一?我感觉泛型现在变得更加重要,部分原因可能是我们现在看到了它的实际用例,而过去它只是“我们希望有,但还不够重要”。

Ian Lance Taylor: 我认为你的看法有一定道理……但我认为我们现在更加关注泛型的另一个原因是,我们终于觉得自己可以掌控这个问题了。当然,大多数人已经很开心地使用 Go 很多年了;我自己也思考泛型问题很多年了,早期的一些提案已经发布,但它们都很糟糕……还有一些没有发布的提案,甚至更糟糕。

有几份未发布的提案,我只是写了出来,自己思考了一下,然后只分享给了几个人,比如 Robert 和 Russ,他们会告诉我,“这个提案很糟糕。”我认为,经过这么多人的帮助,我们终于达到了一个阶段,人们的第一反应不再是“这很糟”,而是“嗯,也许我们可以让它变得有用。”

Johnny Boursiquot: 鉴于泛型引入带来的复杂性问题,你认为当前的提案是否达到了不引入过多复杂性、不给用户增加过多负担的标准,从而保持 Go 的简洁性?我们都非常努力地想要避免一些复杂性进入 Go,你觉得现在的提案达到了这个标准吗?

Ian Lance Taylor: 我觉得你问到了核心问题。我们需要作为一个整体社区来回答这个问题。就我个人而言---是的,我认为我们已经达到了这个平衡。但我不是最终的决策者。我们需要达到一个阶段,让大家能够尝试这个实现,然后看看大家的反馈。

Jon Calhoun: 实现是这里的关键,因为仅仅看提案,你会觉得“嗯,这看起来不错”,但有些东西在你真正深入使用之前,你很难知道它到底会是什么样的感觉,是否直观。因为有些东西从外部看起来很简单,但实际上并不是,而有些东西看起来复杂,但当你使用时,你会发现“哦,这其实很简单。”

Ian Lance Taylor: 是的,我同意。

Mat Ryer: 我们的社区 Slack 频道非常热闹。大家正在实时收听,提问… Marwan 问道:“预计 Go 的编译速度会变慢多少?对此有没有相关的目标?” Dylan Barack 随后补充说,如果编译速度只慢 50% 到 100% 之间,他还能接受,Ian,你觉得这可能吗?

Ian Lance Taylor: [笑] 首先,不使用泛型的 Go 编译速度应该完全不会受到影响。其次,我想说的是---

Mat Ryer: 哦,抱歉,Ian,你指的是编写泛型代码,还是甚至在使用泛型代码时也不会受到影响?

Ian Lance Taylor: 我是说即使在使用泛型代码时也不会受到影响。

Mat Ryer: 好的。

Ian Lance Taylor: 现有的 Go 程序显然没有使用任何泛型代码。所以现有的 Go 程序不会因为语言中添加了泛型而变得更慢。但当前的设计实际上设想了几种不同的编译策略,我们预计如果泛型真的被添加到语言中,我们将不得不通过实验来让编译器根据不同的情况选择不同的策略。

其中一种策略是慢速版本,在这种情况下我们会为每个步骤或类型参数重新编译所有内容。但我认为在大多数情况下没有必要使用这种策略。然后还有一种策略,是类似于我们今天实现接口的方式,但并不完全相同,因为我们不希望有接口那样的分配需求,但仍然是基于类的类型参数来编译。

你知道,最简单的层面上,我们可以根据每种类型有多少指针来描述类型。所以你可以根据不同的指针集为每个泛型函数重新编译。如果你用非常大的类型参数实例化---是的,或许你会为此做一个特殊处理,但这种情况不会经常发生。因此,在这种情况下,你可能会为每个泛型函数编译四次或八次。但这并不意味着你的编译速度会慢八倍,因为大多数函数并不是泛型函数。这只是我们可以使用的几种编译策略之一。

我觉得如果编译器变慢 100%,那将是一个失败。我们不希望编译速度变得那么慢。如果普通程序的编译时间真的增加那么多,我们可能无法推行泛型。当然,你肯定可以写出让编译器变得非常慢的极端程序,但普通情况---

Mat Ryer: 那种我会写的极端程序。

Ian Lance Taylor: [笑] 普通情况下,编译速度不应该变慢 100%。我希望速度只会慢 25% 左右;不过这是我随便说的,因为我们还没有进入真正的实现阶段。

Mat Ryer: 好的,我们不会因此抓住你不放,Ian… 但这确实很有趣。听到你们在为语言添加新特性时要考虑的所有不同因素,感觉很有意思。正如我之前说的,从外部来看,我常常只认为这是语法上的改变,但实际上还有很多其他方面要考虑。我也很好奇,Nathan Youngman 在 Slack 上问道---他没有说明自己多大年纪---在接口和泛型之间是否可能会有一些编译器优化?如果我们最终拥有运行速度更快或性能更好的东西?这是个有趣的想法。

Ian Lance Taylor: 是的,这是个有趣的想法。我以前没有想过这个问题,我也不知道。

Mat Ryer: 很好,我也不知道。

Jon Calhoun: 关于编译时间的讨论非常有意思,正如 Mat 说的,因为有些方面我从未考虑过。我平时不做那些编译时间特别关键的项目,所以即使让我的编译时间延长 10 倍也没啥关系。但有些人肯定不是这种情况… 我可以想象,实施这些新特性并引入新的功能肯定会很复杂。

Mat Ryer: Jon,你会写单元测试吗?

Jon Calhoun: 会啊。

Mat Ryer: 那如果让你的编译速度延长 10 倍呢…

Jon Calhoun: 是的,如果编译变慢了---其实也不会有太大影响。我可能不会在每次改动几行代码后都运行测试,而是只运行一些特定的测试……但对我来说影响应该不大。

Mat Ryer: 是啊,真的很神奇。当我按下保存键时,我会编译并运行测试,如果有测试失败,我会在 IDE 里显示出来。Go 的快速编译时间从一开始就存在,虽然有些波动,但这是另一件我们可能视为理所当然的事… 如果它消失了,我们肯定会想念它。

Johnny Boursiquot: 是的,正如 Ian 所说,如果编译时间有明显的影响,那对于语言、编译器和开发者的工作流程来说都是一个巨大的打击。我不想因为使用泛型而让我的工作流程受到影响。我想这不是任何人希望的。

Mat Ryer: 是的,听起来他们也非常关注这个问题。我同意,这确实很重要。还有一件事,我经常听到有人避免使用 defer,因为 defer 会有一点性能损耗。然后我发现他们的使用场景根本不会受到任何影响。人们有时会对“如何从某些东西中榨取每一点性能”有些执着。实际上,可读性---你作为开发者修复代码时的效率如何?那种性能呢?

Ian Lance Taylor: 是的,完全正确。既然你提到了 defer,我就插一句,在 1.14 版本中,我认为 defer 的性能将大大提升。目前有一些这方面的工作正在进行中。

Mat Ryer: 我之前没为 defer 付费。每次使用 defer 时,我应该付费吗?不过我几乎愿意,因为它真的太好用了… [笑声] 这是我最喜欢的 Go 关键字之一。是啊,这真的很令人兴奋。我喜欢的一点是,当 Go 团队在努力改进标准库、改进编译器工具时,我们不用做任何事情就可以享受这些好处… 所以我真的很感谢你们为我们辛苦工作,感谢你们。

Ian Lance Taylor: 不用谢,不过你知道---很多改进并不是来自 Go 团队。很多改进来自社区中的其他人。我们做了很多协调工作,但很多实际工作是由外部完成的。所以也要感谢所有人。

Mat Ryer: 这听起来很棒。

Johnny Boursiquot: 这真的很酷。还有一点,我认为 Go 2 的版本命名似乎并没有特别的推动力。我认为即将引入的变化,像合同(contracts)和泛型,是向后兼容的,基本上仍然保持了 Go 1 的承诺---你的代码仍然可以正常工作… 我觉得这真的很了不起。

Ian Lance Taylor: 是的,这也是我们一个重要的目标。

Mat Ryer: 这意味着这些改动可以进入即将发布的 Go 版本,而不必等到 Go 2 吗?

Ian Lance Taylor: 是的,Go 2 目前更多的是一个概念。这么说吧,我们会尽量保持 Go 1 的兼容性。如果必须做出某些破坏性改动,那我们可能会做,但我们会尽量避免。也许在某个时候,或许在泛型落地之后,或许在更多错误处理改进到位之后,或许等模块(modules)稳定后,我们会称之为 Go 2。这样做可能是个不错的营销策略,可能听起来比较好;也可能给大家一个重新了解这门语言的理由… 但这并不意味着 Go 1 的程序会停止运行。

我喜欢用的例子是,你写一个 C 程序---不一定是 1970 年的 C 程序,但大概 1980 年的 C 程序今天仍然可以运行;C 语言从未有过一个 C 2,所以为什么不模仿这种模式?它是一种非常成功的语言。

Mat Ryer: 是的,完全同意。事实上,我喜欢 Go 2 甚至可以移除一些东西的想法。但当然,这意味着会有破坏性改动。不过我喜欢看到这样的破坏性改动---当我们让语言变得更简单时。

Ian Lance Taylor: 同意。

Johnny Boursiquot: 比如移除 panic?[笑]

Mat Ryer: 哈哈… 移除全局状态。

Johnny Boursiquot: 这可是挑衅啊。

Ian Lance Taylor: [笑]

Jon Calhoun: 你们越来越贪心了。

Mat Ryer: 是啊。你知道它们引入了随机映射(random to the map),因为人们滥用了它?顺便说一句,我现在就滥用它来获取随机的东西,但我是用另一种方式滥用的。是的… 会是一样的。

Jon Calhoun: 我有一个问题---我们谈了一些关于语言会如何改变的事情,也谈到了当人们接触 Go 后,他们会想要大量使用 Channel,尽管这并不是正确的工具… 我确实有一个担忧,很多人也会有同样的担忧---一旦我们有了泛型,人们就会想要使用它们,所以他们会写出这些泛型实现的数据结构库,或者其他东西… 我认为 Go 社区现在有一个很好的习惯,就是不会随便引入依赖,他们会自己写东西。但如果我们有了泛型,你觉得这会改变这种心态吗?

Ian Lance Taylor: 嗯,我不知道。我觉得这是个好问题。我也不确定。你们觉得呢?

Mat Ryer: 我觉得我们已经有了一个空间,可以讨论包的质量。我看过几次很好的演讲---比如 Julie Qiu 做过一个关于如何有意识地选择依赖的演讲,而不是随便从任何地方拿来一个依赖;她提到应该看看项目是否健壮,是否被广泛使用,是否有测试,API 看起来如何,文档怎么样… 考虑这些因素。这会变得更重要,因为一旦泛型进入语言,大家可能会很容易地去使用这些库,我们会看到大量新的库涌现,做各种很棒的事情,然后我们就会面临选择的难题… 我认为这是一个我们社区仍然面临的普遍问题---不知道哪些依赖可以信任,哪些依赖只是一些不太适合用于生产代码的实验性项目。

Johnny Boursiquot: 我认为这是社区自然会解决的问题。我们之前提到的实践,比如滥用 Channel,或者不论程序是否需要都使用并发… 这些问题我们已经解决得差不多了,社区中也有足够多的材料来教育大家---“尝试这样做,避免那样做,这是基于 X、Y 和 Z 原因。”经过这些年的发展,我们形成了所谓的 Go 习惯用法,基本上是采用某些方法。

我认为没错,刚开始时你会看到大量使用合同(contracts)和泛型功能的东西,但我认为当我们自己踩了足够多的坑之后,情况会逐渐平稳下来,最终形成所有 gopher 都理解的 Go 习惯用法。

但我确实有点担心新手,那些来自其他语言的开发者,或者第一次学习编程的人,他们刚好使用 Go 学习---如何教他们这些概念。因为这需要你在多个层次上思考不同的事情,才能真正理解这些特性在哪里有用。

我认为 Go 博客上那篇 Why Generics 文章做得相当不错,介绍了“这是你现在会怎么做。”你需要为字符串写一个反转函数,为整数写一个反转函数,而泛型可以帮助你去除这些样板代码。

这些材料对于正确教授如何使用这些语言特性至关重要,我认为这也是一个自然发生的过程。

Jon Calhoun: 对我来说,我想到的一个例子是围绕 Go 的路由和 Web 框架… 我觉得如果有类似 Gorilla 工具包的东西,它提供了所有这些不同的 Web 工具,可以使用它们。我想现在可以说它们都经过了充分的战斗测试,是很好的工具。而有一个类似的泛型和数据结构工具包会很有用…

但我也担心你可能会遇到这种情况---我们有 20 种不同的实现,20 个不同的路由器,它们都在相互进行基准测试,关注错误的细节。所以一方面,我确实希望社区能够搞清楚并达成某种共识… 但我也看到其他方面的情况,我并不 100% 确信这会发生。

Mat Ryer: 是的… 但这实际上不是泛型的问题,我想。这是社区的问题。

Jon Calhoun: 这是社区的问题。我只是希望泛型不会让这种情况变得更糟。你会想“路由器有多少不同的东西可以改变呢?”其实真的不多。但在数据结构方面,你可以改变的东西就很多了。

Mat Ryer: 是的,没错。我想我们只能拭目以待。

Mat Ryer: 嗯,没错。我觉得我们还需要再观察一下。我喜欢给出的一个建议---这几乎是我们可以用来检验任何改变建议的非官方测试……我会告诉大家---有人提到他们对数组和切片的困惑,我会说:“现在只需要学习切片,这样你就可以先提高生产力,之后你可以根据需要了解它的底层工作原理。” 这有点像“及时学习”;这是学习的最佳时机,因为你有需要了解它的上下文。

所以我会告诉大家:“不用担心它。” 如果你可以说“别担心它……” 泛型---最新的提案绝对通过了这个测试,“不用担心它。”

像你说的那样,在阅读文档时有几种情况你会看到这些泛型函数---它们看起来有点不一样,所以你需要知道如何调用它们……但尤其是在类型被推断的情况下,你几乎可以忽略它是泛型的事实。这是它的一个优点,所以我认为这肯定会有所帮助。

Ian Lance Taylor: 我当然希望如此。

Jon Calhoun: 那你觉得我们作为社区能否开发一些工具,以便更有可能实现这一点?我在想的是---Mat,你提到过你不想过早进行优化,不想在写一次具体实现之前就把它做成泛型版本……所以如果我们有一些工具能让你轻松地将写好的实现,比如一个自平衡树,转换为泛型版本,这会不会帮助社区避免过早优化?

Ian Lance Taylor: 这是个有趣的想法。我觉得这样的工具应该挺容易写的。至于人们是否会觉得它有用---我不确定。或许吧。

Mat Ryer: Jon,或许你可以贡献一下这个工具。

Jon Calhoun: 你看,Ian 说这个工具很容易写。我怀疑他写起来会比我容易得多。

Ian Lance Taylor: [笑] 不一定。

Mat Ryer: 嗯,我不知道。但我可能会赌他写起来确实容易些。不过我还在想关于处理器和 HTTP 方面的事情,可能会发生什么变化。还有,关于上下文---我们是否会看到带有泛型风格的方法,比如取值……我不知道这是否可行。能否在一个非泛型的类型中仅有一个泛型方法,或者……?

Ian Lance Taylor: 在当前的设计草案中,不,它是不允许的。原因是它使得理解带有泛型方法的类型何时实现接口,或者可能是泛型接口变得更加混乱。我们在这上面遇到很多令人困惑的问题,因此决定---没必要,因为你总是可以写一个泛型函数来代替,所以我们就把方法排除掉了。如果我们以后能更好地理解它,或许可以将其添加到语言中,但我认为它不会出现在第一个版本中。

Jon Calhoun: 我有一个问题。在其中一个例子中,你提到一个契约,比如说是数值类型,或者类似的东西,基本上涵盖了所有不同的整数类型和数字……我猜有时候查看零值是有用的。你觉得---我想问的是,你如何与这些常量值做比较?这会改变编译器处理它的方式吗?还是有一些特别的东西?

Ian Lance Taylor: 不……我觉得,这又回到了你可以使用的不同编译策略。有几种不同的方式可以让编译器处理它。从语言的角度来看,这非常简单。如果契约允许的所有类型都可以与零进行比较,那么你就可以写一个与零比较的代码。至于它究竟如何编译---可能有一些有限的类型,然后你为每种类型编译;或者你使用类似方法的方式,实际上传递“这是你如何与零比较的代码”。我不确定哪种方式在编译时是最佳的。

Mat Ryer: 好的,我觉得今天的时间差不多了。非常感谢我们今天的特别嘉宾 Ian Lance Taylor,他正在致力于泛型提案。Ian,我觉得你做得很好;我个人非常喜欢最新的提案。如果你还没有看过,可以去网上查找一下。而且 Ian 的演讲现在也可以看到了;如果你搜索 “GopherCon Generics 2019”,你会找到 Ian 的演讲。还有 Johnny 的演讲以及我的,但如果我宣传自己的演讲就太不合适了…… [笑声]

Jon Calhoun: Mat 从来不会做这样的事情。

Mat Ryer: 这不是我的风格,这不是我的风格。不过我的书还在卖。 [笑声] 好的……再次感谢我的其他嘉宾,Jon Calhoun,Johnny Boursiquot……先生们,非常感谢。下次见,拜拜!






20240914

Robert 和 Ian 加入我们讨论 Go 中泛型的最新更新。当开发人员开始使用专为试验泛型和 Go 而设计的工具时,他们希望得到什么类型的反馈?讨论泛型的轻量级 Go 论文是怎么回事?为什么我们不能对泛型使用尖括号?

本篇内容是根据2020年7月份#140 The latest on Generics音频录制内容的整理与翻译

过程中为符合中文惯用表达有适当删改, 版权归原作者所有.

Carmen Andoh: Welcome, everybody, to this edition of Go Time. We have a very special episode for you today, the latest on generics. Here with us are Robert Griesemer and Ian Lance Taylor of Google’s Go team. Good afternoon!

Robert Griesemer: Hello, everybody.

Carmen Andoh: And also with us are our hosts with the mosts, Johnny Boursiquot…

Johnny Boursiquot: Hello.

Carmen Andoh: …and Jon Calhoun.

Jon Calhoun: Hey!

Carmen Andoh: And I’ll be running point today, I’m Carmen Andoh. Welcome, again, to Go Time. So I guess let’s start with the updates. We have Ian on here. I think it was about last October, discussing what was then the GopherCon presentation given on templates… And since then, a new draft proposal has come out, so maybe you can talk a little bit about what that update is, Ian?

Ian Lance Taylor: Sure. So Robert and I released the updated design draft for moving forward with generics. The biggest change was that we dropped the idea of contracts, and just decided that instead of having a separate syntactic construct, which was a contract that we could just use interface types to describe the contract between the type argument and the type parameter. A lot of people looking at contracts had seen that they seemed a lot like interfaces, and people had trouble separating out exactly when you would use a contract and when you would use an interface… So we simplified this - and this was, I should say, almost entirely due to Robert - to just use interface types.

And then the second big step we made was we’ve released a translation tool and a type checker. So we have a type checker that works for the design draft, the description of generics in the design draft, so that gives us some confidence that what we have written about can actually work…

[04:11] And we have a translation tool which translates code into ordinary Go. The translation tool is not, by any means, a final thing. There’s cases it doesn’t handle. It’s just an experimental tool, but it lets people actually write code that can actually run using generics. So we can get a feel for whether generics actually works for people, and whether it actually addresses the issues that they have.

Carmen Andoh: Can you talk a little bit about what your understanding is based on the feedback so far of what people’s issues are when it comes to either lack of generics or this current draft proposal?

Ian Lance Taylor: Well, a lot of the feedback has actually been about the syntax, which is sort of simultaneously the least interesting and the most accessible part of the proposal. Obviously, it’s really important to have a good syntax, and we’re really paying attention to the feedback that people have given. We actually now have two possible syntaxes implemented in the translation tool, which Robert did…

And beyond that, there’s the semantics, of course… And I think the feedback on the semantics has been quite positive so far. People have tried out generics, they’ve written some pretty extensive pieces of code, and I think the feedback we’ve gotten there has been uniformly positive. Robert, do you remember any real concern to this point?

Robert Griesemer: No, I think most of the feedback was really based on syntactic issues, and we tried to address this with this alternative that we have now… And we need to play with this a little bit more. And then on the semantics side - of course, there’s not everything ironed out quite yet. We have even mentioned that in the design draft, especially when it comes to type lists and exactly what does it mean to have for instance type parameters in the type lists, and things like that.

So we are in the midst of refining that, but I don’t recall off-hand right now that this was a primary sticking point with feedback from the people.

Ian Lance Taylor: We’re trying to sort of really pin down the real semantics of type lists, as Robert said. But we haven’t had much feedback. There’s been some people wondering about the exact details of embedding a type parameter inside a struct or an interface, which we need to decide precisely what it means… But as a matter of fact, almost no one, in terms of actually using generics in practice.

Carmen Andoh: Speaking of semantics, one of the things that you did in order to inform this new draft proposal version is team up with type theory experts, including Philip Wadler, and you came out with a paper called Featherweight Go, which I don’t understand a word of… [laughter] And I actually am giving a bounty to the entire Go community for anyone who wants to try to demystify that. I know that there is a panel that you were on with Phillip, trying to talk about that paper and pairing up with that… But maybe you can try to demystify Featherweight Go maybe in its essence, for our listeners right now. Do either of you wanna try to take a stab at that?

Robert Griesemer: I can try. Maybe not a real stab, but maybe a little explanation. So this cooperation, I should say, happened because Rob Pike actually reached out to Phil Wadler. They knew each other from way back, and Phil Wadler was interested, and then we started talking with Phil Wadler and with Rob Pike… He didn’t really have time to produce a beta in this, but then Phil Wadler, Ian Taylor and I started talking about what we wanna do… And they have, of course, a strong background in type theory. Phil Wadler has done this – not the same work, of course, but similar work, many years ago, for Java, so he’s really an expert.

[07:56] So now we have a whole team - it’s not just him - that have been working on this Featherweight Generic Go… Which is based on Go, but very much slimmed down. So we now have a language that really only has type declarations and methods. And those type declarations are only interfaces and only structures (structs), and the only thing you can have is methods and interfaces, of course, and methods associated with structs… And inside those methods you can only have basically single functional expressions. So it’s a very, very simplified language, but what you can do is you can invoke a method.

In this paper, he explores two situations this Featherweight Go, which is like the basic Go, simplified Go, without any generics [unintelligible 00:08:45.28] which is that basic, simplified Go, extended with generic features. And those generic features are very much modeled along the draft design, with the exception of type lists. So those type parameters, as in the draft design - the type parameters have what in the paper is called type bounds (we call them now constraints), and there are interfaces, there are also interfaces in the paper, and they basically model in a very simplified fashion what the design draft is trying to do with real Go.

The goal of this paper is to prove, first of all, that this is a sensible design, in the sense that the type system that we’re creating here is sound; you cannot create situations where you could write a program that would be unsound in terms of the type system. And then also, they tried to prove and have proven that it is possible to translate such a simplified generic Go program into a regular Go program, through a process which is called monomorphization, basically expanding everything for every possible instantiation of these generic functions and types… And then they prove that these programs are basically [unintelligible 00:10:06.22]

That’s the gist of this paper, and this gives us very strong confidence that they’re a) not designing somewhere into the blue, and b) that what we’re designing actually makes sense from a type system point of view. We’re not gonna hopefully find problems down the road, where we have some internal inconsistency. So I think that’s really the benefit…

I think it really helped us also understand a little bit better what it means to have interfaces as constraints, and how we need to type-check this. So I think there is a real synergy here.

Carmen Andoh: Very cool. And would you say because of the partnership it helped you understand some of the semantics better in terms of how it informed the newest draft proposal?

Robert Griesemer: Oh, absolutely. I think we had part of a prototype working before this paper was complete, and in the process there’s of course all kinds of questions, and what we sort of invented ad-hoc, they basically did in parallel, independently. Then when we started talking to each other, especially when we went through the individual steps in the paper, we could basically verify that our thinking was matching their thinking, and vice-versa. So our ad-hoc design that was maybe more based on what seemed to be the right thing to do - and not so much driven by a mathematical background - matches… And so that’s great; that means we’re not doing something weird.

Phil Wadler actually took the time himself to walk us through the paper in detail, and this is how our understanding of the paper came about. I’m not a type theory person… So I now feel like “Okay, I have some sort of idea how to read the math”, but I –

Ian Lance Taylor: [12:04] Actually, my name is on the paper, but I have no claim to understanding the paper at all. [laughter]

Carmen Andoh: Okay, you’ve heard it here, folks - it’s not just you; even Ian and Robert had a really hard time with the Featherweight Go paper and all that math notation. This is funny.

Ian Lance Taylor: Let me add that they really helped us with the move away from contracts and towards interface types. They pushed us in that direction, as Robert did as well… So it helped make clear that it would be equally powerful, and it would be usable.

Johnny Boursiquot: It’s worth noting that there is a YouTube video of Phil actually walking through portions of this as well… And I attempted to read the paper; I couldn’t get very far, but I did watch the video and he did an excellent job walking through some of the key concepts there. I believe there is a plan for an expanding on the ideas of Featherweight Go, and I believe there’s a [unintelligible 00:13:00.06] or some other implementations these I imagine are going to build on the foundation you have now, to try and figure out “Okay, what else haven’t we thought of, with the Rust generics, in Go?” Is that correct?

Ian Lance Taylor: Yeah, that’s exactly right. They’re gonna try to add – I mean, as Robert said, Featherweight Go really is very limited, so they’re gonna try to add other features of Go in and make sure that this type system is still sound, and the generic system is still sound. Which we think it will be. But you know, it will be very interesting to see what they come up with.

Johnny Boursiquot: Something you mentioned earlier was that some of the feedback was somewhat superficial, in that it was limited only to the syntax; obviously, there’s a lot more under the hood that really must be solved in order to really have a consistent implementation. But to do that some justice, you have folks that are sort of familiar with a common utilization of angled brackets as the way of specifying the generic types and whatnot… And people were scratching their heads, “Well, first it was in parentheses, now they’re thinking about square brackets… Why can’t we just do angle brackets? What’s the problem?” So maybe if we can just kind of explain the dangers of trying to put that into Go right now - maybe that addresses somewhat some of that early feedback.

Ian Lance Taylor: So Robert had a really good example in the email he sent out about square brackets, showing a case where you really can’t parse angle brackets if you don’t know whether you’re looking at a generic function or type, or whether you’re looking at a pair of expressions that happen to have a comma in the middle because it’s some kind of multiple assignment. So that’s just sort of a fundamentally ambiguous syntax. Robert, do you wanna talk about the importance of parsing without type-checking?

Robert Griesemer: Sure. So even in existing Go we have some situations where we do not know at parse time what we have. The classical example is if we have a conversion or a function call with exactly one argument. So if you say f(x) is this a function call, or is it a conversion? We do not know at parse time. But it doesn’t matter, because we can build the syntax tree at parse time. That’s the only thing that matters. And that syntax tree that has a functor - maybe it’s a type in a conversion… And a list of arguments. That’s all we need. And that’s the same for a function call or a type conversion. And then at type-checking time we can look at this functor and see “Is it a type? Well, then it must be a type conversion.” Or if it’s a function - then, well, it must be a function call. So that’s all jolly.

The problem with the angle brackets is that at parse time we cannot even know how to create a syntax tree in this specific case, especially in that example that we’ve given in the mail. We don’t know how to parse this, so we don’t know how to build a syntax tree, and that means there’s just no way forward to resolve this.

[16:10] One way to go forward would be if we had type information at parse time. In languages such as C++ where they use angle brackets for templates, there is symbol information at parse time, and the parser needs that to make the right decision. But that also means that everything that you will use at a particular place needs to be already declared at that point. And so in C++ you need to make sure that everything that you’re using in an expression has been declared before, some way or another; maybe with a forward declaration of sorts. In Go we cannot do this, because – well, we could, but we don’t have forward declarations in Go, and we don’t want them. In the very first version of Go, that’s never seen the day of light, there was actually forward declarations, but we got rid of them pretty quickly.

In Go you can have a package that is spanning multiple files, and so if you refer to a function in one file, that function may not even be declared yet. It may show up at the very end of the last file that the parser is gonna see. So there’s just no way to have this information available. So without that information we cannot – it’s just not parsable, and there’s no way around it… So angle brackets, as it is right now - it cannot work. So it’s not like we don’t wanna do it, or we don’t like them, it’s just it cannot work with the Go as it is right now.

Break: [17:37]



Carmen Andoh: So what Robert is referring to is rather a discussion thread on Golang Nuts, and it is one of the most recent, and it’s an addendum to their draft proposal, where syntax feedback had been received, and a new addition was adding parentheses. So now it’s a case of getting feedback from the community about whether the preference is for square brackets, versus parentheses.

Robert, or Ian, what are some of the trade-offs for either/or of these, in your mind?

Ian Lance Taylor: The advantage of parentheses I think is that type parameters really are parameters, and type arguments really are arguments… So it makes sense to use a syntax for type parameters that’s similar to the syntax for regular, non-type parameters. [unintelligible 00:19:16.07] And I find the result to be – it sort of feels natural to me, at least… And it reads well. The disadvantage is in a complex generic function you can have lots of parentheses flying around, you can have type parameters, you can have regular parameters, you can have result parameters; they’re all parenthesized lists, and it can get a little hard to see exactly what’s going on.

[19:47] Also, in a call, at the call site, sometimes you pass type arguments, sometimes you don’t, and it can be a little unclear, again, exactly what’s going on. Like, if you had a new function, the new function might take a type, and then you have to have another set of parentheses for regular parameters… So there’s some potential confusion there.

We’ve also discovered certain ambiguities with parsing when using parentheses. Not common cases, but cases that do arise in real code. There were cases where it was ambiguous when you referred to an instantiated type, or an instantiated function, and it was hard to know exactly what was going on.

A simple example would be an embedded field inside a struct. You can embed and instantiate a type in a struct. It’s not really clear whether you’re embedding a type in a struct, or whether you’re doing some other kind of operation there.

So square brackets, by comparison - they’re still a list syntax, so now type parameters and parameters don’t look the same, which for many people is an advantage, but for some people it’s a disadvantage… And then there seem to be at least so far fewer parsing ambiguities when using square brackets.

I don’t have a good feel right now for the sentiment of the broader Go community… There are definitely people who like parentheses and definitely people who like square brackets. I don’t have a clear sense as to one clearly being better than the other, but I have seen a lot of people saying that either could work; that they don’t have any major objections. Do you have anything to add, Robert?

Robert Griesemer: No, I think that’s an accurate description. I think we can confidently say that the square brackets don’t have the ambiguities at this point that we’ve seen with the parentheses. We did not know this in the beginning, we only found out after writing that code, where we ran into problems… But we decided to stick with the parentheses because we wanted to make progress on all the other fronts. The reason for this alternative now is that we’re revisiting this decision so we can make sure that at the end we have looked at all the alternatives.

Johnny Boursiquot: Anecdotally, from what I’ve seen in the Twitterverse and the feeds and all these things, I think there is a penchant towards the square brackets. From what I hear, most think it’s more readily apparent what is going on when you look at the code. You don’t have to do a double-take - “Okay, what’s applying to the parentheses here?” We can more easily, very quickly figure out “Okay, this has to do with the generic type, and everything else is what I would expect.” Basically, that’s my two cents there, from what I’m seeing from the feedback from the community so far.

Robert Griesemer: Great. Thanks.



Carmen Andoh:欢迎大家收听本期的 Go Time。今天我们有一集非常特别的节目,关于 Go 的泛型的最新进展。今天和我们在一起的是来自 Google Go 团队的 Robert Griesemer 和 Ian Lance Taylor。下午好!

Robert Griesemer:大家好。

Carmen Andoh:还有我们的主持人 Johnny Boursiquot……

Johnny Boursiquot:大家好。

Carmen Andoh:……以及 Jon Calhoun。

Jon Calhoun:嘿,大家好!

Carmen Andoh:今天我来主持,我是 Carmen Andoh。再次欢迎大家收听 Go Time。我们就从更新开始吧。我们有 Ian 在这里,我记得大概是去年十月,你讨论了当时在 GopherCon 上关于模板的演讲……从那时起,一个新的草案提案已经发布了,或许你可以谈谈这个更新是什么,Ian?

Ian Lance Taylor:当然可以。Robert 和我发布了关于泛型的更新设计草案。最大的变化是我们放弃了“契约”(contracts)的概念,而是决定使用接口类型来描述类型参数和类型实参之间的约束,而不是使用单独的语法结构---契约。很多人看到契约时觉得它们和接口很像,并且很难区分什么时候该用契约,什么时候该用接口……所以我们简化了这个过程---这几乎完全是 Robert 的功劳---我们决定只使用接口类型。

接下来我们发布了一个转换工具和类型检查器。我们有一个类型检查器,它可以根据设计草案来工作,这给了我们一定的信心,证明我们写的东西是可行的。

我们还发布了一个转换工具,可以将代码转换为普通的 Go 代码。这个转换工具绝不是最终的版本,它有很多情况还无法处理。它只是一个实验性工具,但它可以让人们实际编写使用泛型的代码并运行它。这样我们就可以了解泛型是否真的对人们有用,是否解决了他们遇到的问题。

Carmen Andoh:你能谈谈你对目前反馈的理解吗?人们在没有泛型或当前草案提案的情况下遇到的主要问题是什么?

Ian Lance Taylor:很多反馈实际上是关于语法的,这既是最不重要的部分,也是最容易理解的部分。当然,拥有一个好的语法很重要,我们也非常关注人们给出的反馈。我们现在在转换工具中实现了两种可能的语法,这是 Robert 做的……

除了语法,还有语义部分……我认为到目前为止,语义部分的反馈总体还是比较正面的。一些人已经尝试了泛型,写了一些相当复杂的代码,我认为我们收到的反馈基本上都是积极的。Robert,你还记得有任何实质性的担忧吗?

Robert Griesemer:不,我认为大部分反馈确实是基于语法问题的,我们试图通过现在的这个替代方案来解决这个问题……我们需要再多测试一下。在语义方面,当然,还有一些地方没有完全敲定。我们在设计草案中也提到了这些,特别是在类型列表方面,例如当类型列表中有类型参数时,这意味着什么。

我们正在进一步完善这些问题,但我不记得在反馈中有哪个问题是主要的阻碍。

Ian Lance Taylor:我们正在努力真正确定类型列表的语义,但我们还没有收到太多这方面的反馈。有人对在结构体或接口中嵌入类型参数的具体细节表示疑惑,这些我们需要进一步决定其确切含义……但实际上,几乎没有人在实际使用泛型时遇到过问题。

Carmen Andoh:说到语义,你们为了改进这个新草案提案,与类型理论专家合作了,包括 Philip Wadler,你们还发表了一篇名为《Featherweight Go》的论文,我完全看不懂那篇论文……[笑声] 我为整个 Go 社区悬赏,谁能解密那篇论文。你们还与 Philip 一起参加了一个讨论小组,讨论这篇论文和你们的合作。也许你可以为我们的听众解释一下 Featherweight Go 的本质是什么?你们谁愿意尝试解释一下?

Robert Griesemer:我可以尝试解释一下。这个合作其实是 Rob Pike 找到 Phil Wadler 开始的。他们很早就认识,Phil 对此很感兴趣,我们就开始了讨论。Rob Pike 因为忙没有全程参与,但 Phil、Ian 和我开始就泛型问题进行讨论……他们在类型理论方面有很强的背景。Phil Wadler 多年前为 Java 做过类似的工作,所以他是这方面的专家。

现在我们有一个团队在研究 Featherweight Generic Go,这个语言基于 Go,但大大简化了。它现在只有类型声明和方法,类型声明只有接口和结构体(struct),并且你只能在接口或结构体中定义方法……方法内部只能有简单的函数表达式。所以这是一个非常简化的语言,但你可以调用方法。

在这篇论文中,他探讨了两种情况:一种是 Featherweight Go 的基本形式,没有泛型的 Go;另一种是扩展了泛型功能的 Featherweight Go。这些泛型功能与我们设计草案中的泛型非常相似,不同的是没有类型列表。论文中的类型参数有类型边界(我们现在称之为约束),这些约束也是接口,基本上是对设计草案中泛型设计的简化模型。

这篇论文的目标是证明这个设计是合理的,尤其是我们正在创建的类型系统是健全的,不能编写出违反类型系统的程序。此外,他们还证明了可以通过一种称为单态化的过程,将这种简化的泛型 Go 程序转换为普通的 Go 程序,基本上为每个可能的泛型函数和类型实例化展开所有内容……他们证明这些程序也是可以工作的。

这就是这篇论文的要点,这让我们对设计的合理性非常有信心。我们不会在未来发现一些内部不一致的问题。所以这就是这篇论文带来的好处……

我觉得它也帮助我们更好地理解了将接口作为约束的含义,以及我们如何进行类型检查。所以我认为这是一种很好的协同作用。

Carmen Andoh:很酷。你觉得这种合作帮助你们在语义方面理解得更透彻了吗?这是否对最新的草案提案有直接的影响?

Robert Griesemer:哦,绝对如此。我认为在这篇论文完成之前,我们已经有部分原型在运行了。在这个过程中,我们遇到了各种问题,而我们自己临时发明的解决方案,正好与他们的理论研究平行进行。然后我们相互讨论,尤其是在逐步深入研究论文的过程中,我们能够确认我们的思路与他们的思路是一致的,反之亦然。所以我们的设计虽然是基于直觉和经验,但与他们的数学推导是一致的,这让我们非常高兴,因为这意味着我们没有做一些奇怪的事情。

Phil Wadler 实际上亲自花时间详细向我们解释了这篇论文,这也是我们对这篇论文的理解来源。我不是类型理论专家……所以我现在觉得“好吧,我大概懂了一些数学”,但是---

Ian Lance Taylor: 实际上,我的名字也在那篇论文上,但我完全不敢说我理解那篇论文。 [笑声]

Carmen Andoh:好吧,大家听到了,不仅仅是你们,连 Ian 和 Robert 对 Featherweight Go 论文及其数学符号也感到非常困难。真有趣。

Ian Lance Taylor:我还要补充一点,他们确实帮助我们从契约转向接口类型。Robert 也推动了我们朝这个方向前进……这让我们更加清楚地认识到接口类型同样强大并且易于使用。

Johnny Boursiquot:值得一提的是,YouTube 上有一段 Phil 实际演示这部分内容的视频……我尝试阅读那篇论文,但进展不大,不过我看了视频,他确实很好地解释了其中的一些关键概念。我听说他们还有计划继续扩展 Featherweight Go 的想法,也许有 [听不清] 或其他实现,我想这些都会基于你们现在的基础,继续探讨“我们还有什么没想到的东西,比如 Rust 的泛型在 Go 中的应用?”是这样吗?

Ian Lance Taylor:是的,完全正确。他们会尝试添加---正如 Robert 所说,Featherweight Go 非常有限,所以他们会尝试添加 Go 的其他特性,确保类型系统依然健全,泛型系统依然健全。我们认为它会的,但他们的研究结果还是非常值得期待的。

Johnny Boursiquot:你之前提到的一点是,有些反馈是比较表面的,也就是说它们仅限于语法层面;显然,底层还有很多问题需要解决,才能有一个一致的实现。但是为了公平对待这些反馈,很多人已经习惯了使用尖括号来指定泛型类型……大家会疑惑:“为什么一开始用括号,现在又在考虑方括号……为什么我们不能用尖括号?有什么问题?”也许你可以解释一下为什么在 Go 中尖括号会带来麻烦,这或许可以回应一些早期的反馈。

Ian Lance Taylor:Robert 在他发送的关于方括号的邮件中给出了一个很好的例子,说明了在某些情况下,如果你不确定是在看泛型函数或类型,还是在看一对带逗号的表达式(比如某种多重赋值),你确实无法解析尖括号。这本质上是一种语法歧义。Robert,你要不要谈谈解析时不进行类型检查的重要性?

Robert Griesemer:当然。即使在现有的 Go 语言中,也有一些我们在解析时无法确定的情况。经典的例子是当我们有一个带有一个参数的转换或函数调用时。如果你写 f(x),这是一个函数调用,还是一个类型转换?我们在解析时不知道。但这没关系,因为我们可以在解析时构建语法树。这是唯一重要的事情。而这棵语法树有一个函子---可能是一个类型(在类型转换中)---和一个参数列表。这对函数调用和类型转换都是一样的。在类型检查时,我们可以查看这个函子,看看它是一个类型,还是一个函数。如果是类型,那它就是类型转换;如果是函数,那它就是函数调用。所以这两者都没问题。

问题在于,使用尖括号时,在解析时我们甚至不知道如何创建语法树,尤其是在我们邮件中提到的那个例子中。我们不知道如何解析它,因此也不知道如何构建语法树,这意味着我们无法继续解析。

一种解决方法是,如果我们在解析时有类型信息。在 C++ 等使用尖括号作为模板的语言中,解析器在解析时需要符号信息来做出正确的决定。但这也意味着你在某个地方使用的所有内容必须已经声明了。所以在 C++ 中,必须确保你在表达式中使用的所有东西都已经声明,或者通过某种方式进行前向声明。在 Go 中,我们不能这样做……实际上,我们可以这么做,但我们没有前向声明机制,也不想要它。在 Go 的第一个版本中,确实有前向声明,但我们很快就去掉了它。

在 Go 中,一个包可以跨多个文件存在。如果你在一个文件中引用了一个函数,这个函数可能还没有被声明,可能会在最后一个文件的末尾才出现。所以我们没有办法在解析时拥有这些信息。因此,在没有这种信息的情况下,我们无法解析这些内容……所以尖括号在现有的 Go 中是行不通的。并不是我们不想用它们,而是它们不能在 Go 中工作。

Carmen Andoh:Robert 所提到的,是在 Golang Nuts 讨论组中的一个帖子,这是他们草案提案的最新补充。因为收到了关于语法的反馈,他们增加了括号的选项。现在社区正在反馈他们更喜欢方括号还是括号。

Robert,或者 Ian,你们觉得这两者的权衡是什么?

Ian Lance Taylor:我认为使用括号的好处是,类型参数实际上就是参数,类型实参实际上也是实参……所以使用类似于常规参数的语法来表示类型参数是很有意义的。[听不清] 我觉得这种方式很自然,至少对我来说看起来很顺眼……它读起来也很流畅。缺点是,在一个复杂的泛型函数中,可能会有很多括号飞来飞去,你有类型参数、普通参数、返回参数;它们都是括号包围的列表,有时可能不太容易看清楚发生了什么。

同时,在调用时,有时你传递类型实参,有时却没有,这也会让人有点困惑。例如,如果你有一个 new 函数,它可能会接受一个类型,然后你还需要另一组括号来传递常规参数……这可能会让人感到混淆。

我们还发现了一些使用括号时存在的解析歧义。虽然这些不是很常见的情况,但确实在实际代码中出现过。例如,当你引用一个实例化的类型或函数时,有时难以准确知道发生了什么。

一个简单的例子是结构体中的嵌入字段。你可以在结构体中嵌入一个实例化的类型。在这种情况下,不太清楚你是在嵌入一个类型,还是在执行其他操作。

相比之下,方括号虽然也是列表语法,但类型参数和普通参数的外观不同,这对许多人来说是个优点,但对某些人来说是个缺点……而且目前看起来,使用方括号时遇到的解析歧义要少得多。

目前我还不太清楚 Go 社区的整体偏好……肯定有人喜欢括号,也有人喜欢方括号。但我还没有看到明显的一方胜出。不过我看到很多人表示,无论哪种方式都可以接受,他们没有什么大的反对意见。Robert,你有什么要补充的吗?

Robert Griesemer:不,我觉得这是一个准确的描述。我觉得我们可以自信地说,方括号没有我们在括号中遇到的那些歧义问题。我们在一开始并不知道这些问题,只有在编写代码后才发现……但我们决定继续使用括号,因为我们想在其他方面取得进展。现在提出这个替代方案是因为我们正在重新审视这个决定,以确保我们在最终决定前已经考虑了所有的选项。

Johnny Boursiquot:据我在 Twitter 和其他社交媒体上看到的情况来看,我觉得大家更倾向于方括号。大多数人认为,使用方括号时,代码的含义更加直观,不需要再三确认“这些括号到底对应什么?”我们可以更轻松地理解“哦,这肯定是与泛型类型相关的”,而其他部分则是我们期望的内容。这是我从社区反馈中看到的个人看法。

Robert Griesemer:谢谢你。

Jon Calhoun: So I do have one question on that… Have you guys talked with developers of IDEs or syntax highlighting tools that people use, to see if any of them have feedback on any of this sort of thing? An example is JetBrains - since they have GoLand, I assume that maybe they’d have some feedback on which one is easier to make obvious inside the editor, where people are actually coding… So have you had a chance to talk to people who are developing tools like that, to get their feedback?

Ian Lance Taylor: We have talked to the people who have developed the Go Please language plugin… And from their point of view, I don’t think it matters that much, because they’re just hooking in for the parser, and the parser does support both cases, and the parser is just gonna feed back to them what the code looks like… So they didn’t have much trouble adding parentheses support, and they’ve just recently added square brackets support to Go Please as an experimental thing… So I think at least at that level it hasn’t been a problem. We haven’t talked to JetBrains, though; that’s a good idea.

Carmen Andoh: So more practically, for the Go community - when do you think that you’re going to get enough feedback to move forward with moving from a draft proposal to actually putting it forth as a proposal to change in the language?

Ian Lance Taylor: [23:51] We don’t have any timelines in mind, I’d say. As we mentioned earlier, we’re still trying to pin down some of the precise semantics, which I don’t think is going to affect any existing code; in fact, I’m sure it’s not gonna affect any existing code. We wanna make sure that we understand and we wanna make sure that the multiple Go compilers will all implement the same thing. We’re gonna have to have some sense of how to add to the language spec. So those are the steps we’re looking at now. We’re certainly gonna move forward as fast as we can before making a formal proposal. Of course, at that time none of it will be a surprise. People will have seen all of the ideas already, and we’ll just have to see how it flies. So far, I feel like the reaction has been largely positive, which is encouraging… But I don’t know exactly what the timeline is going to be.

Carmen Andoh: What kinds of feedback at this point are you, Robert and Ian, looking for?

Robert Griesemer: I think we wanna see things that don’t work, and that you would expect to work. We definitely have seen some of these things, which tended to be just bugs in our prototype… And we have spent some time fixing those bugs, and we have slowed down a little bit on that, because it’s just a prototype, and at some point you have to make a call as to how much time you wanna spend on that, and making progress elsewhere… But yeah, generally I think we would like to know “Can you write the kind of generic code that you expect to be writing with this design?” And if not, why not? Let us know. Is there things that you expect to be working, but they don’t? Are they fundamental to our design? And if so, is there something we need to do?

These are the important questions that we should try to answer ASAP, because once we have something more firm, it’s gonna be very hard to make these changes later, if not impossible.

Ian Lance Taylor: Yeah, I totally agree. And I’d say - is there anything you find surprising? If you’re looking at code that’s written using generics and you read that code, is there anything that you just say “I don’t get it”, or “It just doesn’t act the way I expected it to act” - that kind of feedback would be extremely useful.

Jon Calhoun: Were there any specifics that you guys wanted to avoid supporting? Like, do other languages have something in generics that you looked at and decided “This isn’t something we want to support?” or maybe it’s just like a really obscure use case, or something…

Ian Lance Taylor: Well, my favorite example is that C++ templates are in fact a Turing-complete language in and of themselves, which is really cool… And we decided we absolutely did not want to support that, in any way whatsoever. [laughter]

Carmen Andoh: One of the things that I happen to know about both of you is that you have extensive experience in generics coming from other languages… Ian, you were on the C++ readability team at Google, when you famously saw a spec for the Go language and wrote a compiler for it, I think… And Robert, you were on the V8 team that was writing a VM for Java… So are there any things from those lenses of experience where you kind of want to make sure you prevent – and I’m specifically trying to get the angle here of the naysayers who say generics are gonna add complexity to the language. What is your experience about keeping that complexity at Bay in this proposal?

Robert Griesemer: So just to clarify - I was working on V8 in the very beginning for maybe barely a year… I didn’t really do anything with generics in V8. I was on the implementation side. My experience with generics was maybe C++, with templates, and probably the highest point there was when I was able to - as Ian alluded to before, it’s Turing-complete - write a program using C++ templates that would decide whether a constant was a prime number or not… And the compiler would decide it at compile-time. So that’s not the kind of thing we would like to support.

[27:54] With respect to what I’d like to see or not see - honestly, I’m worried about the kind of code that people are gonna write. There’s no question about that. And we see some of the examples that people send us, that cause crashes in the prototype, and they’re just unbelievably convoluted and really hard to decipher… But as other people have pointed out, those people are really pushing the envelope; they’re trying to just see “What can I do with this thing?” And I hope this is not gonna be the kind of code that people are gonna write down the road.

I think one of the first things we need to do if we have this for real - we need to come up with a kind of best practices guide, that guides everybody a little bit as to how you should use generics, and when you should use them and when you should not use them. Very similar to what we developed for goroutines and channels; in the early days of Go everybody was using goroutines and channels for everything, and it took a little while for us to learn where it was appropriate and where it wasn’t.

Ian Lance Taylor: I agree completely. And I’ll add that – I think that one of the things we’ve been really focusing on during this whole multi-year process is to avoid the complexity that exists in C++, and to a somewhat lesser extent in Java… Because those languages are very powerful, and at the same time, they can lead to code that people find difficult to understand. And that’s just not a good fit for Go. And partly, it’s because they are languages that are much more object-oriented than Go is, that have inheritance built into the language, and therefore they have to reflect inheritance in their implementation of generics… Which then leads to considerable complexity in understanding “How do you choose the exact type that’s going to be used to instantiate this C++ template?” And then they also have overloading, so we have to do overload resolution…

These are all really powerful techniques that let people write quite compact code, that can be extremely effective… But at the same time, novices come in and they just don’t understand exactly which type is gonna be used… So we wanted to make sure that we avoided that as much as possible. We wanted to be very clear exactly how a generic function or type was going to be instantiated, and what the type arguments were going to be.

Johnny Boursiquot: It’s just like we saw with the goroutines, and the overuse of channels for everything… It was kind of crazy there in the beginning. But I’m letting you know right now, you are gonna see that surge; there is gonna be that spike where everybody wants to use generics for everything under the sun… And then we’re gonna start walking that back and developing best practices.

I’d definitely like to see some leadership from the Go team on that, maybe an expansion of effective Go on the Go blog, and adding some of the ways to caution, to provide along with that, that says “Hey, this is really the best use for this”, and whatnot. And obviously, I think a lot of the community members are gonna be stepping up and writing blog posts and showing the do’s and don’ts, and that kind of thing… So it will definitely not be all up on the shoulders of the Go team but it is something that I think is to be expected; like any new toy, everybody’s gonna try and abuse it… But it’s all in the spread of implementation, right? I think we are going to develop best practices around what should your generic code look like for a production system, for when you’re the one who steps away from it and somebody else has to step in and understand and read what is going on… I think that’s something that is going to come… I hope.

Ian Lance Taylor: Yeah.

Carmen Andoh: Yeah, I think one of the things that I really like about the Go culture is that we have managed complexity, our idioms through the culture. We have absorbed values and simplicity, and we can continue to do so for generic Go. I think that will be a big part in whether we keep complexity at bay. It’s not necessarily the technical enforcement, but the idiomatic/cultural enforcement. We’ve talked about idioms as culture on this show before, so it’s kind of interesting to see it play out with generics.

[32:02] One of the questions that we had was a forward-looking question, and it’s assuming that this is gonna be a proposal that gets ratified and put into the language… Are there any plans for managing the surge of feature requests for the standard library, now that generic data structure and algorithms are possible? Are you gonna let that happen in the ecosystem?

Ian Lance Taylor: That’s a great question. Are there any plans? I’d say no. There are not at this point any plans… But there will be plans. The experimental translation tool does come with a tiny little set of sample libraries. When I wrote those, I viewed those as kind of prototypes for what we might wanna add to the standard library going forward. I don’t think they’re great examples or anything, but I think that they can sort of show areas where we might wanna add new standard library packages, and show possible implementations as subject to people really looking at them and making sure they make sense. I don’t expect there to be a lot of additions to the existing standard library packages. There might be a few, but most of the existing standard library packages were written without generics and they work fine.

So yeah, there may be a lot of people saying “What’s that? Generics?”, but you know, the truth is they work already, and we don’t need to add generics there. I think it’s more gonna be a matter of adding some new packages to really take advantage of generics. The translation tool, for example, has a slices package, which has various functions that operate on slices of any type… And it has a chans package that operates on channels of any type. This is the kind of code which we aren’t able to write with Go today, but we are able to write with generics… So I feel like that’s gonna be where we’re gonna add to the standard library. We’re not gonna be moving fast on any of this, for sure… But you’re right that we should develop some kind of framework for how we’re gonna add packages.

Break: [34:00]

Jon Calhoun: One of our listeners in the Slack had asked “While you’re collecting feedback, is there a good time or a good expectation for measuring how build speed changes over time?” Especially now that things are experimental, I assume that it’s not really fair to assume that that’s exactly how things are gonna be whenever it actually ships… So what should people expect and when is the right time to give feedback on that?

Ian Lance Taylor: [36:11] Okay, that’s a great question. Yeah, the experimental tool has no similarity whatsoever to any real implementation. We know it’s slow, and it’s gonna be slow, and that’s just inevitable… If this does move forward to become a proposal and it gets accepted, then most likely the implementation will be to start with a branch of the main Go toolchain, and we’ll start adding generic support on that branch, which will involve changing the compiler mainly, and any other changes to other tools that are required… So that’ll be the time to start giving feedback about changes to build speed.

We’ve talked about it with some of the compiler developers, like Keith Randall especially, and we think we can do it without a significant increase in build speed. I mean, there will be some increase in build speed; we don’t think it’s gonna be a huge increase… But you know, this is really speculative at this point. So the time to give that feedback is when we’re able to start doing development… And hopefully, people will also be able to contribute work when we start doing that work on the public branch.

Carmen Andoh: We can ask other questions there on the Slack channel… “When generics are released, would it be released together with the idea of best practices?” What they call it here is a standardized collections interfaces, to avoid fragmentation… “Or do you think this will not be a problem, for example size versus count versus length?”

Ian Lance Taylor: Yeah, it’s a little hard for me to visualize exactly what a standard collection interface will look like in terms of Go. Clearly, it exists in C++, and maybe we can just borrow what they do. I’m not quite sure exactly how that’s gonna work. But certain things are pretty straightforward. I think iteration is the more complicated one. C++, in part because of the sophistication of the template capabilities is able to have a generic iteration interface that works for any collection, and I’m not sure that we’re there yet with our less powerful and less complex… On the other hand, I don’t know that anyone’s really looked at it in detail, so maybe we can make some progress in that area.

Robert Griesemer: There’s also a difference in the sense that in Go we already have, for instance, maps, which of course is not the whole collection hierarchy, but it’s a significant chunk, which doesn’t exist in C++, so there is a temptation there to build this very complete hierarchy… While in Go in many cases we just use a map for various things, and that works fine; and the map isn’t sometimes generic already. But of course, that’s not to say that this would replace a more comprehensive package perhaps…

Ian Lance Taylor: It’s a great question though.

Robert Griesemer: I think it’s gonna be interesting to see what people will do with this. The design - we tried to make it as orthogonal as possible to the rest of the language. That really means adding generic type parameters somewhere adds a new dimension to the kind of programs you can write. So it really opens up an entirely new dimension of possibilities, and it will be very interesting to see what people do with this.

Carmen Andoh: One other comment from the Slack is a technological guard rail, if you will… So for the language, currently govet and golint function as sanity checks we depend on. What is your opinion on adding new checks added to discourage the use of generic code in this toolchain, if all you need is a non-generic version?

Robert Griesemer: Well, we can easily make the compiler two times slower for each additional type parameter; that would limit the complexity quite quickly…

Johnny Boursiquot: [laughs]

Carmen Andoh: It’s really gonna have to probably be enforced through culture, or these best practices and idioms, right?

Robert Griesemer: [39:47] It’s quite easy to see that programs have one or two type parameters and functions… But if it goes over two or three, then you are starting to wonder “What’s happening here? Is this really necessary? Is this really good?” So I would say there’s some immediate questions right there, when you look at code like that. But I’m shooting from the hip here; I’m just guessing here. And I suspect that there may be things we can say “This is not good.” And maybe such things can go into a vet check eventually, but I don’t know what that would be at this point.

Jon Calhoun: I suspect things like the Go Proverbs will sort of help on this front, just because – like, we have the ones like “A little copying is better that a little dependency”, I think is one of them… And I think if people stick with that mindset of copying an entire type – like, if you’re only using two different versions of it, or an entire function or whatever it happens to be, it’s better than writing the generic version for just two different types. But if you do find yourself in a situation where you need to use it for 5-6 different types, then generics might actually be the right solution.

Robert Griesemer: Mm-hm.

Jon Calhoun:我有一个问题……你们有和 IDE 开发者或者语法高亮工具的开发者交流过吗?比如 JetBrains,他们有 GoLand,我想他们或许会对哪种方式在编辑器中更直观有一些反馈……你们有机会和开发这些工具的人交流,听取他们的意见吗?

Ian Lance Taylor:我们有和开发 gopls 语言插件的人交谈过……从他们的角度来看,这并不重要,因为他们只是连接到解析器,而解析器支持两种情况,解析器会将代码的样子反馈给他们……所以他们在添加括号支持时没有遇到太大麻烦,最近他们也在 gopls 中以实验的方式添加了方括号支持……所以至少在这一层面上,这不是问题。不过我们还没有和 JetBrains 交谈过,这确实是个好主意。

Carmen Andoh:对 Go 社区来说,比较实际的问题是---你们觉得什么时候能收到足够的反馈,进而从草案提案推进到真正的语言变更提案?

Ian Lance Taylor: 我们目前没有具体的时间表。如我们之前提到的,我们仍在努力确定一些精确的语义问题,我认为这些不会影响现有的代码;事实上,我确信它不会影响现有的代码。我们想确保我们理解这些问题,并确保多个 Go 编译器能够实现相同的功能。我们还需要有一些关于如何将其添加到语言规范的概念。这是我们目前正在进行的步骤。我们当然会尽快推进,直到正式提出提案。当然,到了那时,这一切都不会是个惊喜。大家已经看过所有的想法了,我们只需要看看它如何表现。到目前为止,我觉得反馈总体上是积极的,这令人鼓舞……但我不确定具体的时间线会是什么样子。

Carmen Andoh:在这个阶段,你们,Robert 和 Ian,希望收到什么样的反馈?

Robert Griesemer:我认为我们想看到那些不工作,但你期望它们工作的情况。我们确实已经看到了一些这样的情况,通常只是我们原型中的一些 bug……我们花了一些时间修复这些 bug,不过我们已经减缓了修复速度,因为这只是个原型,某个时候你得决定在这个上面花多少时间,并在其他地方取得进展……但总的来说,我认为我们想知道“你能否用这个设计写出你期望的泛型代码?”如果不能,为什么不能?请告诉我们。是否有你期望能工作的东西却没有工作?这些问题是否对我们的设计至关重要?如果是,我们是否需要做些什么?

这些是我们需要尽快回答的重要问题,因为一旦我们有了更确定的东西,之后再进行这些更改就会非常困难,甚至是不可能的。

Ian Lance Taylor:是的,我完全同意。我还想说---是否有让你感到意外的情况?如果你在看使用了泛型的代码,是否有任何地方让你感到困惑,或是代码没有按你预期的方式运行?这种反馈将非常有用。

Jon Calhoun:你们有没有特意避免支持的内容?比如说,其他语言的泛型中有些什么是你们看了之后决定“这不是我们想要支持的”,或者是一些非常小众的用例……?

Ian Lance Taylor:嗯,我最喜欢的例子是 C++ 模板实际上是图灵完备的语言,这真的很酷……但我们决定绝对不支持这种功能,绝对不支持。 [笑声]

Carmen Andoh我知道你们两个都有丰富的泛型使用经验,来自其他语言……Ian,你曾在 Google 的 C++ 可读性团队工作,当时你看到 Go 语言的规范后写了一个编译器……而 Robert,你曾在 V8 团队中编写 Java 的虚拟机……所以从这些经验中,你们是否有一些特别想避免的东西---我特别想从那些反对泛型的人角度来问,他们认为泛型会增加语言的复杂性。你们在这方面的经验是什么?在这个提案中是如何避免复杂性的?

Robert Griesemer:首先澄清一下---我在 V8 团队工作过,时间很短,可能不到一年……我在 V8 中真的没有做过任何与泛型相关的事情。我更多是做实现方面的工作。我的泛型经验更多来自 C++ 的模板,可能最高峰是我用 C++ 模板写了一个程序,它可以在编译期间确定一个常量是否是质数……这不是我们想要支持的那种东西。

关于我希望看到或不希望看到的内容---老实说,我有点担心人们会写出来的代码,这毫无疑问。我们看到了一些人发送给我们的例子,这些例子导致了原型中的崩溃,它们是令人难以置信的复杂,真的很难理解……但正如其他人所指出的,这些人实际上是在挑战极限;他们只是在测试“我能用这个做些什么?”我希望这不是人们以后会写的那种代码。

我认为,如果我们真的发布了泛型,我们首先需要做的事情之一是制定一个最佳实践指南,指导大家如何使用泛型,什么时候应该使用,什么时候不应该使用。类似于我们为 goroutine 和 channel 制定的那些最佳实践;在 Go 的早期,大家使用 goroutine 和 channel 做所有事情,花了一段时间我们才学会哪里合适,哪里不合适。

Ian Lance Taylor我完全同意。我想补充一点---在整个多年的过程中,我们一直在努力避免 C++ 中的复杂性,Java 中也有类似的复杂性,虽然程度稍轻……因为这些语言非常强大,同时,它们可能导致人们写出的代码难以理解。这对 Go 来说并不适合。部分原因是因为它们比 Go 更面向对象,语言中内置了继承,因此它们必须在泛型的实现中反映继承……而这又导致了理解“如何选择用于实例化 C++ 模板的确切类型”的复杂性。此外,它们还有函数重载,所以我们还得做重载解析……

这些都是非常强大的技术,可以让人们写出非常紧凑且有效的代码……但同时,初学者进入时,他们根本无法理解到底会使用哪个类型……所以我们尽可能地避免了这种情况。我们希望非常明确地知道泛型函数或类型将如何被实例化,类型参数将是什么。

Johnny Boursiquot:这就像我们在 goroutine 和 channel 的过度使用中看到的情况……在最初确实有点疯狂。但我现在就告诉你们,这种情况肯定会再次发生;会有一波浪潮,大家会想要用泛型做所有事情……然后我们会慢慢开始退回,逐步制定最佳实践。

我真的希望看到 Go 团队在这方面起到一些领导作用,或许可以扩展 Go 博客上的 Effective Go,加入一些关于如何谨慎使用泛型的建议,指出这些功能的最佳使用场景。我相信社区里的很多成员也会站出来,写博客文章,展示应该如何使用和不该如何使用……所以这肯定不会全压在 Go 团队的肩上,但这是大家应该预期到的事情;就像任何新玩具一样,大家都会尝试滥用它……但最终的实现会决定一切。我认为我们会围绕泛型代码的最佳实践进行发展,特别是如何编写适用于生产系统的泛型代码,当你离开后,别人需要接手并理解代码时,这一点非常重要……我希望如此。

Ian Lance Taylor:是的。

Carmen Andoh我非常喜欢 Go 文化的一点是我们通过文化来管理复杂性,形成了简洁的惯用法,并将这种价值观融入了文化。我认为我们可以继续这样做,推广泛型 Go。这将成为我们是否能控制复杂性的重要部分。这并不一定是技术上的强制,而更多是惯用法/文化上的约束。我们之前在这个节目中讨论过“惯用法即文化”,所以看到它在泛型上也有体现,真的很有趣。

我们有一个前瞻性的问题,假设这个提案被批准并且被纳入语言中……你们是否有计划应对未来对于标准库中集成泛型数据结构和算法的功能请求激增?你们会让这些请求在生态系统中自行发展吗?

Ian Lance Taylor:这是个好问题。目前有没有计划?我想说没有。目前还没有任何计划……但肯定会有计划。实验性的转换工具带有一小部分示例库。我写这些代码时,将它们视为未来可能添加到标准库中的原型。我不认为它们是很好的例子,但我认为它们可以展示我们可能会添加新标准库包的领域,并提供可能的实现供大家审查。我不认为会对现有标准库包进行大量添加。或许会有一些,但大多数现有的标准库包是没有泛型编写的,它们运行得很好。

所以,是的,可能会有很多人说“泛型是什么?”但事实是它们已经可以工作了,我们不需要在那里添加泛型。我认为更多是要新增一些包来真正利用泛型。例如,转换工具有一个 slices 包,它包含了对任何类型的切片操作的各种函数……还有一个 chans 包,它对任何类型的通道进行操作。这类代码目前在 Go 中无法编写,但在泛型中是可以的……所以我觉得这将是我们可能会往标准库中添加的地方。我们肯定不会在这方面快速推进……但你说得对,我们应该制定某种框架来指导如何添加新的包。

Jon Calhoun:我们 Slack 中的一位听众问道:“在你们收集反馈时,有没有一个合适的时间或期望,来衡量编译速度的变化?”尤其是现在这些功能是实验性的,我想大家不应该认为它们的性能就是将来发布时的样子……所以人们应该期待什么,什么时候是反馈的合适时机?

Ian Lance Taylor:这是个好问题。是的,实验工具和任何真实的实现没有任何相似之处。我们知道它很慢,而且肯定会很慢,这是不可避免的……如果这个提案推进并被接受,那么最有可能的实现方式是从主 Go 工具链的一个分支开始,我们会在那个分支上开始添加泛型支持,主要是修改编译器,以及其他需要修改的工具……届时将是开始对编译速度变化进行反馈的合适时机。

我们和一些编译器开发者讨论过,比如 Keith Randall,尤其是他,我们认为可以在不显著增加编译速度的情况下做到这一点。我的意思是,肯定会有一些编译速度的增加;我们认为不会是巨大的增加……但这现在还只是推测。所以,给出反馈的合适时机就是当我们开始进行开发时……希望大家也能够在我们开始在公共分支上工作时贡献力量。

Carmen Andoh:我们可以在 Slack 频道中继续问其他问题……“当泛型发布时,是否会与最佳实践的概念一起发布?”这里他们称之为标准化的集合接口,以避免碎片化……“或者你认为这不会成为问题,比如 size、count 和 length 的差异?”

Ian Lance Taylor:是的,我很难想象 Go 中的标准集合接口到底会是什么样子。显然,C++ 中存在这样的接口,或许我们可以借鉴他们的做法。我不完全确定这会如何运作。但某些事情是相当简单的。我觉得迭代是更复杂的一个问题。C++ 部分因为模板能力的复杂性,能够拥有一个适用于任何集合的泛型迭代接口,而我不确定我们是否能达到那个水平,因为我们的泛型功能不如他们的强大和复杂……不过另一方面,我也不知道是否有人真的详细研究过这个问题,所以也许我们可以在这个领域取得一些进展。

Robert GriesemerGo 和 C++ 之间也有一些不同的地方,比如 Go 已经有了 map,当然这不是整个集合层次结构,但它确实是一个重要的部分,而 C++ 中是没有 map 的,所以在 Go 中我们经常用 map 解决各种问题,这样做其实很好;而 map 在某种程度上已经是泛型的了。当然,这并不是说它可以替代一个更全面的包……

Ian Lance Taylor:这是个很好的问题。

Robert Griesemer:我觉得很有趣的是,看看人们会用这个做些什么。设计上我们尽量让它与语言的其他部分保持正交性。这意味着在某个地方添加泛型类型参数,会为你能编写的程序增加一个新的维度。所以它真的打开了一个全新的可能性领域,看看大家会怎么利用这一点将会非常有趣。

Carmen Andoh:Slack 中的另一个评论是关于技术上的护栏……目前,govetgolint 作为 static checks 非常有用。你们怎么看待在工具链中添加新的检查,来劝阻那些本可以用非泛型方式实现的泛型代码?

Robert Griesemer:嗯,我们可以轻松地让编译器对每个额外的类型参数变慢两倍;这会迅速限制复杂性……

Johnny Boursiquot:[笑声]

Carmen Andoh:这很可能需要通过文化或这些最佳实践和惯用法来实现,对吧?

Robert Griesemer:很容易看到程序中有一个或两个类型参数的函数……但如果超过了两个或三个,你就会开始怀疑“这到底在做什么?这真的有必要吗?这真的好吗?”所以我会说,当你看到这样的代码时,会立刻有一些疑问。但我现在只是即兴发挥;我只是在猜测。我怀疑可能会有一些我们可以说“这不太好”的情况。也许这些东西最终可以进入 vet 检查,但我现在还不知道那会是什么。

Jon Calhoun:我猜类似于 Go Proverbs(Go 箴言)这样的东西会在这方面有所帮助……比如我们有一个箴言是“少量的复制胜过少量的依赖”,我认为如果人们坚持这种心态,如果你只是针对两种不同的类型使用泛型,那还不如复制整个类型或函数。但如果你发现自己需要为 5-6 种不同的类型使用同样的代码,那么泛型可能确实是正确的解决方案。

Robert Griesemer:嗯。



帮我整理这一期英文播客,翻译为通顺的中文,请保留完整内容,不要删减,谢谢

Johnny Boursiquot: The rule of three still applies here… I think we’ve discussed this previously on the show; usually, I don’t even start to think about abstracting away or making a generic version of anything until I’ve seen at least three instances of it, if you will. Then I start to say “Okay, I’m starting to see the edges here. I’m starting to see what is the likelihood of this thing being used in the future.” Because any code you write - again, we write the code just once, but we have to maintain that code, possibly indefinitely… So the same rule around those decisions we’re making today, asking ourselves these kinds of questions - I think it’s the same thing we should apply here.

But again, you are gonna see folks who are anti-generics in our community. I’d say it’s like a 50/50 split. We have people who love Go because of the lack of generics… Which is a very specific reason to love a language, but hey… And then you have others who are like “Hey, if Go had generics, I’d be using it.” So it’s kind of an odd mix, but again, I think it’s gonna be up to the community as a whole to come up with what are the idiomatic ways really we wanna treat generics.

Jon Calhoun: I think those would be nice too, because if we have a concrete implementation to point to, even if you just copy it to the docs, for the comment, and say “This is a generic implementation of this concrete thing”. I think anybody who is unfamiliar with generics, or a little bit newer to programming, could look at that and have a much better chance of understanding the code.

Carmen Andoh: One of the things I think about for generics if this does get put into the language is whether or not it’s going to bring on what I would call the would-be gophers. Right now we have the die-hard gophers, people who love Go and use Go and maybe have internalized the no-generic philosophy of Go… And having generics or implementing generics into the language successfully may bring new gophers to the table, and those new gophers are gonna be your .NET and your C++ and your Java gophers. Robert and Ian, do you have any thoughts or suggestions on how to help them keep Go Go, while also giving them this tool that they’ve leaned on so heavily on coming from their other language?

Ian Lance Taylor: Yeah, I hope they will be able to carry their programming practices over where those practices make sense… Where they make sense for Go, I should say. I can’t tell whether adding generics will make it that much more appealing to C++ or Java programmers; I certainly hope it will… But whether it really will in practice - it’s just hard to know. It’s still gonna be a different language, of course.

Certainly, there have been people in the past who have just rejected Go outright because it doesn’t have generics… But I don’t think there have been that many people who take that point of view, and I hope that those people will take another look when generics comes out. But from my point of view, I want Go to be open to everyone. I want all these people to find Go to be a productive language. It’s not really a matter of hauling people from the other language communities… I think generics is interesting mostly because, as Robert says, it’s orthogonal; it lets us write code that we couldn’t write before, in Go. It lets us write code that solves problems in a way that we couldn’t really solve before, at least not without going through type reflection, or massive copying, or whatever.

[44:12] So it’d be great to open more people; I hope that more people keep writing Go… But I think our main interest is to give people another powerful programming tool. At least my main interest.

Robert Griesemer: Yeah, I’d add on that that, again, Go is not now becoming the generic language. It’s just yet another mechanism in the language, like we have interfaces, we have methods… It doesn’t mean now you have to write everything in an object-oriented style. Go has always been multi-paradigm; we enable different ways of programming, and we encourage people to choose the right approach for the problem at hand… So in a situation where a generic approach might be the right approach, then by all means, go for it; and if it’s not, don’t do it.

Of course, there’s gonna be people that really like that playing with types, and more so maybe than getting that code to just do what it’s supposed to do. For them it’s, of course, appealing to have this new mechanism. But again, if your goal is to get something done at the end of the day, use the mechanism, the tool that fits your needs and solves the problem in the greatest way.

Johnny Boursiquot: Along those lines, that’s actually a good segue for the segment of the show where we talk about unpopular opinions.

Jingle: [45:37] to [45:52]

Johnny Boursiquot: So what I’d like to do is ask each one of you to spill onto the mic your unpopular opinion. It could be related to what we’ve been talking around generics, or it could be something that you’re seeing out there that perhaps you have a different opinion from.

Jon Calhoun: Or if it helps, some people have just told us they think buses are more efficient than other forms of transportation in New York City. [laughter] So it can literally be anything.

Robert Griesemer: Well, let me start on that… So I cannot opine on the buses in New York City; I’m a big fan of public transportation, even though this is perhaps not the right time to advocate for them, for other reasons… But unpopular opinions - I don’t know if it’s an unpopular opinion, but I like short identifiers. I do.

Carmen Andoh: [laughs]

Robert Griesemer: And I feel like the closer they are to where you use them, the shorter they can be. And the further away they are from where you use them, the longer they should be. And then there’s some exceptions, like when an identifier is really important in your package and prevalent, then it can be one letter, even if it’s a global. And the most prominent example for that is perhaps testing.t.

Jon Calhoun: I’m not sure if you’re allowed to be a teacher now.

Johnny Boursiquot: [laughs]

Carmen Andoh: Uh-oh… Why, Jon?

Jon Calhoun: Every teacher expects you to write really long, self-explanatory variable names, regardless of where you use them – at least that was my experience. I felt like every teacher wanted long variable names.

Robert Griesemer: So I will comment on your change list if it uses – in a simple for loop, if the iteration variable is called “index”, I will probably comment on that. Call it i, or j, or whatever… [laughter]

Johnny Boursiquot: Ian, what have you got?

Ian Lance Taylor: [47:48] Okay, I don’t know if this opinion is unpopular, but I feel like I write it a lot, so there’s certainly people who don’t seem to grasp it… And that’s that the language is not perfect, but every change to the language carries a heavy cost. So when you wanna come and argue for why the language should be changed – and we see that a lot; I’d say that there’s probably one a day suggestion for some way to change the Go language… Don’t just talk about how it makes the language better, but also spend some time to talk about how it makes the language worse. Because there’s no such thing as a 100% good change to the language. Or I shouldn’t say there’s no such thing; maybe it’s out there. Maybe no one has thought of it yet. But probably, it’s a good bet that all the 100% good changes to the language have already been made. So when you wanna change the language, spend some time to think about how it makes things worse, as well as how it makes things better.

Johnny Boursiquot: I think I just saw Ian drop a metaphorical mic… [laughter]

Robert Griesemer: We’re gonna go back now and think a lot about how generics make things worse… [laughter]

Ian Lance Taylor: I think we’ve got plenty of people telling us that already…

Jon Calhoun: I was gonna say, if you haven’t gotten that feedback at this point…

Carmen Andoh: Well, it has been a pleasure… On behalf of my co-hosts, Jon and Johnny, we thank you, Ian and Robert, for taking the time to talk about the new draft proposal on generics with us.

Ian Lance Taylor: Yeah.

Robert Griesemer: It’s my pleasure.

Ian Lance Taylor: It’s been fun, thanks for having us.

Outro: [49:30]

Jon Calhoun: Carmen, I’ve heard a lot of if’s from you today.

Carmen Andoh: Some what?

Jon Calhoun: A lot of if’s from you today.

Carmen Andoh: What kind of if’s?

Jon Calhoun: “If this makes it into the language…”

Carmen Andoh: Well, okay - I think partly because of what I have learned with try last year… In that a lot of time was spent really thinking about this and trying to solve a good problem, and then a lot of the community felt like it was a fait accompli. And I probably am speaking for Robert and Ian here when I say if a lot. I think that the draft proposal is explicitly meant to not seem as a fait accompli.

Robert Griesemer: Yeah. I mean, if there’s something we learned from that is that we cannot just come out with a it doesn’t matter how well thought out idea, and put it out there and say “So, yes or no?” It’s just not gonna work. There needs to be an educational process, as we had to go through with too, and that we’re trying to achieve now through being more open and going in smaller steps and getting everybody on board.

It’s kind of strange, because programming language evolution is really a social process. It doesn’t actually matter if you have – if you have seen the light and you know exactly the perfect language, you would just put it out there. And you know, maybe it’s 20 years ahead - nobody would even buy it, because people would not see the reasoning why you got to that point.

So you really have to get everybody along – and some people may already be where you are, and some people may not, but you have to get everybody along in little steps, and that’s how we eventually end up where we wanna be… And we can see this with all kinds of things, like garbage collection. Garbage collection was invented in 1950-something, with Lisp. The first Lisp had garbage collection; it was in 1958, I believe… And it’s taken forever before it became accepted as something that a mainstream programming language should have. Maybe Java was the first one that really made it mainstream… And now this is not something that is still disputed or debated, I should say, but it’s not as outrageous anymore. I think that’s true for a lot of things.

Johnny Boursiquot: 三法则在这里依旧适用…… 我想我们之前在节目中讨论过这个问题;通常我不会考虑抽象或制作通用版本,除非我已经见过至少三种类似的情况。然后我才会开始说,“好吧,我开始看清楚边界了,我开始看到这个东西未来可能会被使用的概率。” 因为任何我们写的代码---我们只写一次,但可能需要无限期地维护…… 所以我们今天做出的这些决策和我们问自己的这些问题,本质上也是一样的。

但你会看到我们社区里有些人是反对泛型的。我觉得这大概是五五开吧。有些人喜欢Go语言就是因为它没有泛型…… 这是一个非常具体的理由去喜欢一门语言,但也无妨…… 另一些人则是“嘿,如果Go有泛型,我就会用了。” 所以这有点奇怪的分歧,但我觉得最终还是要由整个社区来决定我们希望如何以惯用的方式使用泛型。

Jon Calhoun: 我觉得这也不错,因为如果我们有一个具体的实现可以作为参考,哪怕你只是把它复制到文档或注释里,说“这是这个具体事物的泛型实现”。我觉得那些对泛型不熟悉或编程经验较少的人,看了这些内容后会更容易理解代码。

Carmen Andoh: 我在想,如果泛型真的被加入到语言中,会不会带来我称之为“潜在的Go开发者”的人。目前,我们有忠诚的Go开发者,这些人喜欢并使用Go,或许已经内化了Go没有泛型的这一哲学…… 而成功地将泛型引入语言可能会吸引新的Go开发者,这些人可能是.NET、C++和Java开发者。Robert 和 Ian,你们有什么想法或建议,如何在给他们熟悉的工具的同时,保持Go的特点?

Ian Lance Taylor: 是的,我希望他们能够在适合Go的地方,将他们的编程实践带进来。至于泛型是否会让C++或Java开发者对Go更有吸引力,我不敢确定;但我确实希望如此…… 不过实际情况如何,很难说。毕竟,它仍然是另一种语言。

当然,过去确实有些人因为Go没有泛型而直接拒绝了Go…… 但我不觉得这样的人有很多,我也希望这些人能在泛型推出后重新考虑Go。但从我的角度来看,我希望Go对所有人开放,我希望所有人都能发现Go是一种高效的语言。其实,这并不是说要把其他语言社区的人拉过来…… 我觉得泛型有趣的地方主要是,正如Robert所说,它是正交的;它让我们可以写出以前在Go中无法编写的代码。它让我们可以用一种新的方式解决问题,至少不必再通过类型反射或大量复制来解决问题。

所以能够吸引更多人是很棒的;我希望更多人继续编写Go代码…… 但我主要的兴趣是为大家提供另一种强大的编程工具。至少这是我的主要兴趣。

Robert Griesemer: 是的,我想补充一下,Go现在并没有变成一门“泛型语言”。它只是语言中的另一个机制,就像我们有接口、方法一样…… 这并不意味着你现在必须用面向对象的风格来编写所有的代码。Go一直是多范式的;我们支持不同的编程方式,并鼓励大家根据手头的问题选择合适的方法…… 所以在某些情况下,泛型方法可能是最合适的,那么就尽管使用;如果不是,那就不要使用。

当然,也会有些人特别喜欢玩类型,甚至比让代码如期工作更感兴趣。对他们来说,显然这个新机制是非常有吸引力的。但如果你的目标是最终完成某件事情,那就使用最适合的机制或工具来解决问题。

Johnny Boursiquot: 说到这里,正好是一个很好的过渡,进入我们节目中关于“不受欢迎意见”的环节。

Johnny Boursiquot: 我想请每个人分享一下你们的不受欢迎的意见。这可能与我们讨论的泛型有关,也可能是你在其他地方看到的某些事情,或许你对此有不同的看法。

Jon Calhoun: 如果你觉得难以开口,有些人曾经告诉我们,他们认为纽约市的公交系统比其他交通方式更高效。 [笑声] 所以可以是任何话题。

Robert Griesemer: 好吧,那我就先来吧…… 我无法对纽约市的公交系统发表意见;我个人是公共交通的忠实粉丝,尽管现在可能不是推广这个的最佳时机,出于其他原因…… 关于“不受欢迎的意见”---我不知道这是否算不受欢迎的意见,但我喜欢简短的标识符。我真的喜欢。

Carmen Andoh: [笑]

Robert Griesemer: 我觉得标识符离使用的地方越近,就可以越短。离得越远,标识符就应该越长。 当然也有一些例外,比如在你的包中非常重要和常见的标识符,它可以是单个字母,即使是全局的。最明显的例子可能就是testing.t。

Jon Calhoun: 我不确定你现在还能不能当老师。

Johnny Boursiquot: [笑]

Carmen Andoh: 噢噢…… 为什么,Jon?

Jon Calhoun: 因为每个老师都希望你写非常长的、能自解释的变量名,不论你在哪儿用它们---至少这是我的经历。我觉得每个老师都希望变量名很长。

Robert Griesemer: 所以如果在一个简单的for循环里,迭代变量叫“index”,我大概会评论一下这个。叫它i,或者j,或者别的什么…… [笑声]

Johnny Boursiquot: Ian,你有什么想法?

Ian Lance Taylor: 好吧,我不知道这是否是不受欢迎的意见,但我觉得我经常说这句话,所以肯定有人没能理解…… 那就是,语言是不完美的,但每次对语言的更改都会伴随巨大的代价。所以当你想为改变语言辩护时---我们经常看到这样的情况;我敢说每天都有一个关于如何改变Go语言的建议…… 不要只谈论它如何让语言变得更好,还要花点时间讨论它如何让语言变得更糟。因为不存在100%好的语言更改。或者我不该说“没有”,也许它存在。也许还没人想到它。但大概可以肯定的是,所有100%好的语言更改已经被实现了。所以当你想修改语言时,花点时间思考它是如何让事情变得更糟的,同时也思考它如何让事情变得更好。

Johnny Boursiquot: 我觉得Ian刚刚打了个比喻式的麦克风…… [笑声]

Robert Griesemer: 我们现在要回去好好思考一下泛型如何让事情变得更糟…… [笑声]

Ian Lance Taylor: 我觉得已经有很多人告诉我们这一点了……

Jon Calhoun: 我正想说,如果你还没收到这种反馈……

Carmen Andoh: 好吧,这次对话很愉快…… 我代表我的联合主持人Jon和Johnny,感谢Ian和Robert抽出时间与我们讨论关于泛型的新草案提案。

Ian Lance Taylor: 是的。

Robert Griesemer: 我的荣幸。

Ian Lance Taylor: 很有趣,谢谢你们邀请我们。

Jon Calhoun: Carmen,今天我听到你说了很多“如果”。

Carmen Andoh: 什么?

Jon Calhoun: 很多“如果”。

Carmen Andoh: 什么样的“如果”?

Jon Calhoun: “如果这被加入到语言中……”

Carmen Andoh: 嗯,好吧---我想部分原因是我去年从try中学到的…… 很多时间都花在认真思考并试图解决一个好问题上,但社区中的很多人觉得这是一种既成事实。我大概也在为Robert和Ian说话,当我说“如果”时。我觉得这个草案提案明确地不想显得像是既成事实。

Robert Griesemer: 是的。我想,如果我们从中学到了一件事,那就是我们不能只是拿出一个再怎么成熟的想法,放在那里说“是或否?” 这根本行不通。需要有一个教育过程,就像我们也经历了这种过程一样,我们现在正在通过更开放、更小步伐的方式来实现这一点,争取大家的认同。

这有点奇怪,因为编程语言的演进实际上是一种社会过程。即便你已经“看见了光明”,知道了完美的语言是什么样的,你把它拿出来,也可能没人买账,因为人们无法理解你是如何得出那个结论的。

所以你真的需要让大家一起前进---有些人可能已经站在你的位置,有些人还没有,但你需要带着大家一起一点点前进,这样我们最终才能达到我们想要的地方…… 这种情况我们在很多事情上都能看到,比如垃圾回收。垃圾回收是在1950年代发明的,最早的Lisp就有垃圾回收机制;那是在1958年,我记得…… 但直到很久以后,它才被主流编程语言接受。也许Java是第一个真正把它推向主流的…… 现在这已经不是什么争议问题了。我觉得很多事情也是如此。