Go 终于在 1.18 喜迎泛型,在它正式 release 之前忍不住玩了一下,然后跟 ahxxm 一起写了一个名字非常普通(当然跟 lodash 没有关系,我学的是 dash.el)的工具库 go-dash。
Go 与泛型与我
第一次用 Go 可能要回到 5 年前,刚从一个 Android/iOS Developer 转型,给公司写一个新的后端项目。在用习惯了充满语法糖的 kotlin 和 swift 之后再来使用这样一个“大道至简”的语言确实不太习惯。其中的一点当然就是泛型。关于泛型的讨论在 Go 社区应该从未停止过,这当中还有不少支持者是让我比较难以相信的,这也算是我第一次接触到“语言宗教”这样一个概念吧。不过这就扯远了,我简单说一下我不喜欢的理由吧。
没有泛型必然导致很多“泛用”方法需要针对不同类型重复定义,在没有泛型的时候,大概有这样三种 workaround(AFAIK)。而这三种方法和我不喜欢的主要理由如下:
- 标准库作弊,比如说
append
,len
等。相当于是对于特定数据结构进行了“特殊处理”,如果你有类似的自定义数据结构,却没办法保证一致的处理模式。 - 使用
interface{}
然后通过反射处理。既享受不了动态语言的好处,却要承担动态语言的坏处。 - 代码生成。虽然重复,但又没那么重复。生成的代码是代码还是数据?应不应该放在仓库里?
他们还有一个共同的问题就是会导致代码的可读性或可浏览性变差,虽然没有盲目使用其他语言的“设计模式”来得伤害那么大,但也算是相当难受了。咳咳,阴阳怪气就到这里吧。毕竟后来官方就宣布“已经在做了”。
快进到前段时间突然在 HN 看到了 go-generics-example,发现本来预订要在 Go 2 出现的泛型,竟然在 Go 1.17 已经可以实验性开启,也就是说不需要等到很大概率不会保持向后兼容的 Go 2 了。
Go Dash, Go
泛型成为 a thing 之后,似乎 Golang 团队对如何修改标准库开始犯难了,作为一个长期向后兼容的语言,如何合理的进化标准库确实是一个很难的话题。详情可见这些讨论:
抱着知道写这些代码多半不会有太大用处的心情,我们还是从 clojure.core 以及 clojure.core.async 中精心(随机)挑选了一些 API 之后,就开始模仿(抄袭)了。
Go 1.17 中的实验性泛型功能无法定义 public function, 所以需要用到 gotip 来获取 master 分支的 Go. 比较惊喜的是 gopls 也同步更新着,所以工具支持上没有任何问题。
go get -v golang.org/dl/gotip gotip download gotip get golang.org/x/tools/gopls@master golang.org/x/tools@master
在
发稿日之前,2022/3/15 Go 1.18 正式 release 了,所以 gotip 不再必要。
Gerrit What
Play and Learn
Go 其实不算是一个能让人感觉 exciting 的语言,增加泛型这次算是个例外吧。写这个没用的库倒是也能学到一些东西,比如在 pkg.go.dev 可以随意申请添加 package。看到 go doc 生成的 html 之后我对从注释生成文档也没那么讨厌了(治好了我的 javadoc 后遗症)。
不过比起这些我更希望标准库能尽快更新泛型版本,以后堆代码也能开心一点。就目前来看 1.18 里包含的 golang.org/x/exp 只能算聊胜于无吧,并且也不太可能在生产代码中使用。