本篇内容是根据2024年6月份#319 Is Go evolving in the wrong direction?音频录制内容的整理与翻译.
两位主播 一起讨论了 Go 社区的一些最新消息, 探讨了是否有些软件不应该用 Go 编写,他们对 Go 是否朝着正确的方向发展的看法,以及常用名词是否可以作为好的软件包名称。还讨论了新版本Go中对于迭代器和go:linkname的改动.
最后还谈到了Rust语言, 对Rust的一些宣传方式及"重写万物?"表达了一些反感..
过程中为符合中文惯用表达有适当删改, 版权归原作者所有.
Kris Brandow: 欢迎收听 Go Time。我是主持人 Kris,今天我们要讨论一些关于 Go 的新闻和文章。上次我们做这个类型的节目时,收到了大家非常好的反馈,看来你们很喜欢那一集……不确定这次会不会有那么多笑点,但我们可以试试看。今天和我一起主持的是 Ian。Ian,你今天怎么样?
Ian Lopshire: 我很好。不过我觉得我们可能会有点想念 Johnny 的笑声,但我们会尽力的。
Kris Brandow: 没错。我们还是会尽量让节目保持有趣和好玩的氛围。首先,我们要讨论的文章名为「Go 在朝错误的方向演变」。文章的作者认为,Go 语言变得越来越复杂,他不喜欢这种复杂性。他特别指出了 1.18 版本中的泛型和即将在 1.23 版本中引入的迭代器。Ian,你怎么看?
Ian Lopshire: 我觉得我有些不同意他的观点。我承认 Go 变得更复杂了,但我认为泛型是一个必要的功能,虽然增加了复杂性,但这是值得的。特别是在日常使用中,你可能根本不会注意到这些变化。
至于迭代器,我有点怀疑,但也非常期待。我已经玩过一些了,函数签名确实有些复杂,也有些让人困惑……但我一直希望 Go 有一个标准的迭代方式。所以我不确定你的看法是什么?
Kris Brandow: 泛型的确从语言规范的角度增加了复杂性……但老实说,我还没有看到有人滥用这个功能,也没觉得它让事情变得更糟。对于普通的 Go 用户来说,我不认为这增加了很多复杂性。更多的是,如果你想实现或维护 Go,可能会有点复杂。比如,我现在经常用 slices 包,它让事情变得更简单,比如 slices.Clone,这个功能以前要写几行代码,现在只要调用这个函数就行了。
至于迭代器,我也不太确定我的感受。一方面,我很喜欢这个想法,但我也理解他的担忧,特别是关于复杂性的问题。以前 for
循环很简单,但现在里面可能会有阻塞操作,比如网络请求。不过,我认为这只是我们需要适应的事情。现代工具可以很好地提示你这行代码到底在做什么。
设计过带迭代器的项目后,我觉得这会是一个更好的解决方案。现在标准库和 Go 包中常常使用不同的迭代模式,比如有的用 next
函数,有的用对象和错误的组合,模式非常不一致。新的迭代器可以让这种情况变得更一致。
Ian Lopshire: 是的,我觉得这可能会像泛型一样,虽然你在日常代码中不常见,但在库的实现中你会频繁使用它。所以它可能会是这样的情况。
Kris Brandow: 我对推/拉语义也有些保留意见……我还没有深入研究最新的实现,但我不觉得它会让语言变得更差或更复杂。除了你提到的会有某些隐藏的函数调用之外,我不觉得它增加了太多复杂性。
Ian Lopshire: 是的,我觉得错误信息和堆栈跟踪可以解决这一问题。也有人说这会让 return
、continue
、break
等语句变得隐式,但我认为这反而简化了代码。现在在自定义迭代器中使用 continue
是个噩梦。
Kris Brandow: 没错。我最近在用 Goldmark 包,它有一个 AST,你需要返回一些特殊值来表示 "继续" 或 "退出",让我觉得有点怪异。而且错误处理也很麻烦。我觉得如果能用常规的 for
循环,情况会好很多。
Ian Lopshire: 也有一种说法认为这增加了一种新的迭代方式,但我觉得这个说法站不住脚。以前并没有标准的迭代方式,现在我们有了一个统一的模式。
Kris Brandow: 是的,我们在试图整合,给大家提供一个预定义的路径。你依然可以选择别的方式,但这很可能是大家最熟悉的方式。
Ian Lopshire: 总结一下,我不觉得 Go 的演变方向是错误的……
Kris Brandow: 说 Go 在「错误的方向演变」是一个非常大胆的说法。语言要保持向后兼容性,必然会变得更复杂。但通过复杂性,我们也可以获得简单性。比如泛型带来了 slices 包的简化,让我们不再需要自己实现一些复杂的功能。
Ian Lopshire: 没错,我喜欢你提到的「小规模复杂性 vs 大规模简化」
。虽然语言本身变得更复杂,但整个生态系统变得更简单了。
Kris Brandow: 是的。文章的结尾提到了 Go 在性能上输给 Rust 的问题,他希望 Go 添加更多的 SIMD 指令优化。但我不觉得 Go 是为了追求极致性能的语言。更多时候,我选择 Go 是因为它适合维护大型代码库。
Ian Lopshire: 这也引出了下一个话题……
Kris Brandow: 是的,下一个话题来自 Reddit 上的一个帖子,讨论「不应该用 Go 写什么软件」。一些热门评论提到了垃圾回收的问题。有些人说垃圾回收是实时系统的障碍,但我认为 Go 的垃圾回收性能已经很强了,现在最长的停顿也只有 100 微秒左右。
Ian Lopshire: 是的,我理解他们的观点。但我不会用 Go 来写嵌入式固件,这些系统对实时性要求极高。不过,我觉得很多人对「stop the world」的垃圾回收机制有误解。
Kris Brandow: 没错。如果你的垃圾回收出了问题,通常意味着你在内存管理上做得不好。垃圾回收并不意味着你可以忽略内存管理,只是你不需要手动写太多代码来管理它。
Ian Lopshire: 另一个热门评论是「不要用 Go 写图形界面」,我大致同意这一点。虽然现在有一些不错的库,但我还是会选择其他工具。
Kris Brandow: 是的,图形界面最好用平台原生的工具来写。不过如果你不想为多个平台写不同的代码,Go 也不是不可以,但有很多更好的选择,比如 React Native 或 WebView。(译者注: 竟然不推荐都出自谷歌的Flutter..)
Ian Lopshire: 还有人提到嵌套很深的 JSON 解析问题,我觉得这是因为我们缺乏好的自定义 JSON 解码工具。Go 中的默认方式是使用结构体来映射 JSON,但当你遇到复杂的嵌套时,这会变得很麻烦。
Kris Brandow: 我同意,如果没有更好的工具,处理这些嵌套的配置格式会非常头疼。XML 也是类似的情况,虽然解析 XML 没什么问题,但如果你需要处理 XML 的命名空间等高级特性,那就麻烦了。
Ian Lopshire: 是的,处理 XML 的命名空间非常繁琐。Go 缺少一些处理这些情况的好库。
Kris Brandow: 没错,这和我们讨论的深层嵌套 JSON 是同样的问题。如果我们没有好的工具来处理这些复杂情况,开发体验就会很差。
Ian Lopshire: 我特别喜欢这段对话中的一句话:「不要用 Go 来写 Excel 宏。」
Kris Brandow: 啊,是的,有人说「我宁愿用 Go,也不愿用 VBA」,我觉得……虽然可以理解,但还是算了吧。
Ian Lopshire: 没错,别写 Excel 宏,它们太危险了。
Kris Brandow: 哈哈,确实,Excel 宏确实不太好。不过,Google Sheets 的自定义函数还挺好用的,但那是用 JavaScript 写的。你知道 Excel 宏是用什么写的吗?是只能用 VBA 吗?
Ian Lopshire: 我也不确定,不过好像主要是 VBA。
Kris Brandow: 是的,我觉得你也可以用 C#
或其他语言写,但大多数情况下还是 VBA。如果你只关心 Windows 平台,可能可以用 C#
,但我不太清楚。我不写 Excel 宏,我喜欢电子表格,但不至于那么喜欢。(译者注: C#
早已经跨平台了吧..)
Ian Lopshire: Excel 宏工程师,听起来是个有趣的职位。
Kris Brandow: 这可能意味着——嗯,整个世界是靠电子表格运转的,所以我相信很多人都有很多自定义的宏来让一切正常运作。但对啊,这个回应列表上还有什么吗?哦,有人说他们用 Go 来写所有的东西……哦,天啊……
Ian Lopshire: 我其实是在尝试回答这个问题---
有没有某类软件不应该用 Go 来写?我觉得没有。
Kris Brandow: 对,我也觉得没有。我觉得可能有些软件用其他语言写会稍微好一点,但……这也是我最近一直在思考的问题。这可能是另一个播客话题,或者是另一个播客集的主题……不过如果有时间我们也可以聊聊,我最近在读很多 Leslie Lamport 的文章(译者注:2013年图灵奖得主,更多可参考天神荟萃--计算机领域的人类群星闪耀时(下篇)")),主要是因为我很久没有读 Paxos 论文了,《兼职议会》的那篇论文。我好像在找他写的另一篇文章,然后我发现了---
基本上,Leslie Lamport 有一个非常长的他所有作品的列表,目前大概有 193 篇论文。所以他在职业生涯中写了很多东西。在他为 Paxos 和《兼职议会》写的介绍中,他说“哦,这是个简单的算法。”他还说“我不明白为什么人们理解不了这个算法。”
所以基本上,我读完那篇论文后,我也觉得“哦,这确实挺简单的。”然后我又找来了他的一些其它论文,我心想“我要把这些都读完。”其中有一篇论文是关于如何教授并发的,他给出了如何教计算机科学和计算机工程学生并发的建议……在那篇论文中有一句非常经典的话……实际上,让我找出来,这样我可以准确地读给你听。他在讨论计算时说,“我们应该如何描述计算?大多数计算机科学家可能会把这个问题理解为‘我们应该使用什么语言?’”我觉得这其实也是我们在问的问题,比如“有哪些东西你不应该用 Go 来写?有哪些东西你不应该使用这种语言来做?”
然后他说,“想象一下艺术史学家如何回答‘你该如何描述一幅印象派画作?’他会说‘用法语’。”这句话非常有趣,而且是对我们这个行业的一个深刻而微妙的批评……因为这是真的。我们常说“你不应该用那种语言来构建那个东西。”这就像在说“你不应该用法语写那篇小说,因为这不好---
你应该用英语写所有的小说,因为英语是写小说的好语言。”我们不应该以这种方式思考问题。他的观点是我们应该用概念来讨论这些东西。我觉得这也适用于我们现在的讨论,我觉得没有什么东西是你---
如果你能用任何语言写出来的东西,你就能用 Go 写出来。Go 是一个图灵完备的语言,这意味着它与其他所有语言是等价的。可能会缺少一些 API,你可能需要通过 FFI(外部函数接口)与 C 交互,或者做一些其他奇怪的汇编工作……但没有什么是你只能用某种语言做而不能用 Go 做的,或者只能用 Go 做而不能用其他语言做的。
所以我觉得更重要的是概念,问题在于你能否在 Go 中很好地表达那些概念……不过还有另一个问题,如果你在创业,或者正在创建一个项目……Go 是你最擅长的语言吗?那你可能应该用你最擅长的语言来写。
比如我们之前谈论 GUI 时,如果你需要构建一堆 GUI,而 Go 是你最擅长的语言,那么也许做一个稍微丑一点的 GUI 并学习类似 Fyne 的东西会更好,这样你可以用它来实现一个 GUI,然后再继续做你想做的应用,而不是为了构建 GUI 去学习一门全新的语言。比如“哦,我得学 Swift、C#,然后可能还得学 C++ 来为 Linux 做 GUI……我不知道 Linux 的 GUI 是用什么做的,但……你要学三种其他语言来表达同一个概念,这有点荒唐。就像你想写同一个故事,但你得用四种不同的语言来写。这感觉有点奇怪,如果你能用熟悉的语言写出来,而且它还能被大家访问到,那就好了。”
Ian Lopshire: 又回到了“在软件工程中提问”,答案就是“看情况。”
Kris Brandow: 对,对。我猜这个问题的答案是“你不应该用 Go 来写什么软件?”大概就是“我不知道,直接用 Go 写吧。”如果你熟悉 Go,Go 是你最擅长的语言,那就用 Go 写。如果因为某些原因它不行,那就换别的语言。但我觉得---
对,我不认为有任何广泛的答案适用于这个问题。这种问题没有那种可以简短回答的广泛答案。如果有人说“你不能用它构建实时系统”,那就不对。这是需要细微差别的问题。
Ian Lopshire: 比如,如果我在写 Spark 任务,我不会用 Go 来写……因为 Spark 只支持 Python 或者 Scala。我觉得这里有个工具的问题。不要因为你想用 Go 就硬把它塞进没有工具支持的东西里。
Kris Brandow: 对。我觉得这也取决于你的目标……因为如果你这么说,那就永远不会有这个工具了
Ian Lopshire: 对,没错,没错。
Kris Brandow: 如果没人去做,那这个工具永远不会存在。这就造成了那些人为的障碍。我觉得连以这种方式思考问题本身都表明了 Leslie Lamport 在他的讨论中,或者在那篇论文中提到的问题。如果我们用“某些语言适合某些事情”这种框架来思考问题,那我们实际上并没有真正关注问题本身。我们是在以一种扭曲的方式看问题,比如“这个问题在 Go 中怎么样。”然后如果我们换成另一种语言,就得以不同的方式解决这个问题……这不是最好的情况。但这是一个更大的行业层面的问题,我们得解决。
Ian Lopshire: 对,我同意你的看法……但我觉得双方都有些回旋余地。最终,你得赚钱,你得完成工作。
Kris Brandow: 对。
Ian Lopshire: 你得完成工作。我不知道……
Kris Brandow: 对……我想再说一次,宏观视角和微观视角,从个人或者小公司的角度来看,可能确实有些东西你不应该用 Go 来写,你得做出权衡。但这也是另一个层面的不同问题。向公司提问和向个人提问是不一样的。所以对,你是对的……看情况。看情况。
Ian Lopshire: 看情况。[笑]
Kris Brandow: 天啊……
Ian Lopshire: 所有问题的答案:看情况。
Kris Brandow: 对。Changelog 那边有一整系列的节目。我想是 Changelog & Friends 系列。主题就是“看情况”。我做了一集,很有趣。还有很多其他集。
Ian Lopshire: 我还没听过呢。我得去听听。
Kris Brandow: 你们都应该去听听。那些节目不错,很有趣。好了,我们还有什么关于“你不应该用 Go 来写什么软件”的话题要说的吗?
Ian Lopshire: 这是一个自己选择的冒险。我确实觉得这引出了一个有趣的话题---
用 Go 写的 makefile 替代品。你应该用 Go 写一个 make 的替代品吗?我觉得大概应该。
Kris Brandow: 对。对,我是说,make 本身因为很多原因确实很糟糕。它是个标准,这点不错,但它只用 tab,还有很多其他奇怪的语法规则,这让 make 和 makefile 非常费劲……[笑] 对于老牌项目,或者像 C 这种更成熟的生态系统,使用 make 更合理。但对于 Go 来说,makefile 总感觉很尴尬、很怪异。所以我通常会选择其他替代品。
Ian Lopshire: 你用过文章里提到的那些替代品吗?
Kris Brandow: 我用过 Mage 几次。我好像从来没用过 Taskfile。而且我讨厌 YAML,所以我觉得我不会用 Taskfile。
Ian Lopshire: 对,我们公司大量使用 Mage……我觉得它的一个好处是我不用去学习那些奇怪的……extrincicities?这个词怎么读……?
Kris Brandow: 哦,extrincicities。对。
Ian Lopshire: AWS CLI 来做事情。Go 有一个很棒的 AWS SDK,所以我们可以在 Mage 文件中做所有自动化的 AWS 工作,而且它非常好用。真心推荐
Kris Brandow: 那很不错。对,而且能够把所有东西都保持在 Go 里听起来也不错。我是说,能用一个真正图灵完备的语言来做事情确实很好,而不用像作者指出的那样,为了让东西在 shell 里运行而做各种奇怪的弯曲操作,特别是在 make 的那个奇怪的 shell 版本里。
Ian Lopshire: 对。能写真正的 for 循环真的很不错。虽然 shell 也能写 for 循环,但我每次写都弄错。
Kris Brandow: 是啊,真正的 for 循环,还有日志记录,执行并行命令,以及很多其他东西,都比在其他地方做简单多了。所以对,我是完全支持像 Mage 这样的工具的。再说一次,对于 Taskfile,我看不出有什么特别之处……我猜如果你不想学 make 的语法,而且你对 YAML 满意,那它是不错的选择……但我真的非常不喜欢 YAML。它有太多奇怪的地方。
我那天在 Markdown 里写 YAML,结果遇到了一个奇怪的错误,因为---
哦,因为我在写的时候,它把我的 tab 转成了空格,或者空格转成了 tab;我也不确定是哪种情况。但 YAML 解析器不喜欢它没有采用正确的缩进类型,结果报了一个奇怪的错误。我当时想“这真烦人。我不喜欢这样。”我真的不喜欢强调空格的语法。所以我可能会直接使用 make。或者干脆用 Mage,或者类似的工具,而不是 Taskfile。但我相信很多人喜欢 Taskfile。
Ian Lopshire: 是的。如果我们要继续批评 TOML 或 YAML,我觉得我最沮丧的一天就是因为 YAML 没有把我以为是字符串的东西当作字符串。所以你不需要给某些东西加引号,对吧?但有时候这会让你陷入困境。
Kris Brandow: 对,确实……这是一种很混乱的标记语言。我理解为什么会创造它,但有很多我们创造的东西有时候都是坏主意。在这个行业里我们创造了很多坏主意。但……对,我觉得 Mage 很酷。我过去用过几次。如果你还没试过,听众们,你们应该去试试。
Ian Lopshire: 推荐……
Kris Brandow: 对,尤其是你可以有一个文件目录,这样你就可以合理地管理它的结构,并在你的代码库中构建自己的小型构建系统。我觉得这很有帮助。
Ian Lopshire: 对。我们甚至在 GitHub Actions 里用 Mage 来管理一些构建和测试脚本。而且它运行得非常好。
Kris Brandow: 对。就像你在构建自己的小型构建系统,如果你只写 Go,那是个很不错的工具。对。对,我不知道,我们还有什么要说的吗,关于 Mage 或者这些 makefile 的替代品?
Ian Lopshire: 我就这些了。[笑] 强烈推荐。如果它适合你,那它真的很棒。
Kris Brandow: 对。
Kris Brandow:我们接下来的文章标题是《Go 1.23 中的一个早该解决的问题》。这篇文章有点“深度技术”类型,讲的是一个叫 go:linkname
的编译器指令,Go 团队正在对其进行严格限制。
Ian Lopshire:嗯,我们要先解释一下这个指令是做什么的吗?
Kris Brandow:对,对,我正准备解释。go:linkname
实际上允许你引用一个通常无法访问的对象或符号。比如,你可以用它来访问其他包中的未导出的变量或函数。假设你有一个包里有一个叫 foo
的函数,通常只能在这个包内部调用,而你有另一个包,你可以用 go:linkname
指令来做“其实我想在我的包里调用 foo
。”显然,Go 团队和 Go 本身出于多种原因需要这种功能,但这也有点不太好,因为这意味着你本打算私有的东西不再真正私有了,你可以随意伸手进去把它拿出来用。
Ian Lopshire:对,有几点需要注意。你得导入 unsafe
包。 (译者注: 详情可参考go:linkname)
Kris Brandow:对。
Ian Lopshire:所以 Go 团队告诉你“嘿,这个功能要失效了。”
Kris Brandow:对,他们正在做的改动是,你不能再随便从任何包里拿东西了。在 Go 1.23 版本中,只有那些明确标记为允许你这么做的东西,才允许你这样做……我觉得这有点搞笑,因为这就像是另一种导出东西的方式。
Ian Lopshire:对,这确实有点奇怪。
Kris Brandow:对,我理解我们为什么要这么做,但这种操作确实有点滑稽。
Ian Lopshire:你经常在标准库中看到这种情况,从 runtime 包中拉取东西,对吧?如果他们要限制这个功能,我很好奇为什么不直接使用一个专门的 runtime 包来拉取这些东西?可能有一个很好的理由,但我不清楚是什么。
Kris Brandow:对,我也觉得这个指令可能本来是用来做其他事情的,结果人们发现可以用它来做这个事情,然后就说“哦,我就用它来做这个吧。”
有趣的是,作者偶然发现这个问题的原因是 听不清 00:38:29.22 包需要获取 TLS 1.3 的默认密码套件……在 Go 中,有一个明确的选择是不让这些密码套件可配置。因为你的硬件是否支持 AES,你的客户端是否想使用 AES,还有很多其他因素,所以默认的密码套件可能并不是一个可以稳定引用的东西……
这个奇怪的情况有趣之处在于,之所以这个变量没有暴露出来,是因为它非常复杂,直到运行时才能确定,甚至在运行时可能还会发生变化。所以这就变成了一个非常奇怪的空间,这也是它没有被导出的原因……但对,这就是一些奇怪的地方。整个情况都很奇怪。
不过,对,这个小改动可能会开始破坏一些随机的东西。我敢肯定,很多人曾经用这种方式来绕过某些问题,现在这些东西会开始坏掉,人们得想办法修复它。
Ian Lopshire:对。而且我觉得这个提议也很有趣,因为 Russ Cox 直接说“嘿,这会弄坏一些东西,但这让我们走上了一条好路,所以值得破坏这些东西。”我觉得我同意这个说法。
Kris Brandow:对,我是说,再一次,我们可能不应该随便伸手进入其他包并摆弄它们未导出的东西。这通常不是我们希望在语言中发生的事情。不过,对,我不知道我还有没有别的要说的了。你还有什么想说的吗?
Ian Lopshire:没什么了。我只是觉得这种情况很有趣,尤其是它在标准库之外也能用。
Kris Brandow: 对,我觉得很多这些指令都是有趣的小细节---
再一次,我明白为什么这些东西是必要的。我觉得 go:linkname
在你想引用汇编中的某些东西时也经常用到。你可能有一个用汇编实现的函数,用这个指令,它就会去抓取那个汇编代码。当链接器在工作时,它会把这些东西连接在一起,即使没有 Go 版本的那个汇编代码。所以我想这种用法应该会继续保留。但有趣的是,这个功能曾经是可以用的,而且现在仍然可以用。
Ian Lopshire:我觉得对于这个提议的另一个有趣之处是,在 1.23 版本中引入了用于迭代器的协程功能,对吧?你不再需要调用一个单独的 goroutine,再同步……而且他们说这些新的协程功能现在或将来都不会允许通过 linkname
引用。所以他们也通过这次改动关闭了这个功能。
Kris Brandow:嗯……很有趣。对,这种小改动可能只会影响到 Go 社区中非常小的一部分人,但在未来可能会产生深远的影响。好的……我想我们列表中的最后一篇文章叫《Go. 不要用普通名词来命名包》。我不确定是否同意这篇文章的观点。文章大意是作者建议“为包找一个不同的名字,一个更长的名字,一个由多个名词组成的名字。”他们举了 time-rate
包的例子,这是 golang.org/x
里的一个包。他们基本上说“rate 是一个非常好的变量名,所以它不应该用作包名,应该叫 rate-limiter
。”但我不太同意这个观点。我觉得仅仅因为某个名字可能是一个好变量名,并不意味着你不应该用它来命名包。因为说到底,包本质上也是变量。
Ian Lopshire:对……我同意这个观点。但是,我确实有好几次不小心把一个变量叫 URL
,而当时我已经导入了 URL
包,然后我就在想“为什么这不工作?”
Kris Brandow:对。但你还能怎么叫 URL
包呢?
Ian Lopshire:我也没答案。但你怎么命名你正在构建的那个 URL 呢?
Kris Brandow:叫 U
。
Ian Lopshire:对,你说得对。
Kris Brandow:[笑]
Ian Lopshire:我不知道。在这篇文章的后半部分,他说他在所有的内部包中通常会用一个字母作为前缀,比如用 p
代表 Crunchy 平台(译者注: 应该是一家Postgres服务商)。我倒不讨厌这个做法。
Kris Brandow:我其实不太喜欢这些包的名字。把包命名为 server
、db
或者 client
感觉很懒散。如果你的代码库没有一个明确的命名结构,感觉代码的结构不太好。如果我看到一个 client
包,我会想“这是什么?”我猜如果你用导入路径的其他部分来说明这是哪种客户端,那还可以。但我不喜欢有很多小包的做法。我更希望有一个大包,比如一个平台包,里面有客户端,而不是叫 pclient
。但我猜有时候你确实不得不为某些原因用这样的名字,不过我觉得这不是一个好的模式。
Ian Lopshire: 对,这在错误包里经常出现。我们的很多服务都有一个 [听不清 00:44:14.13] 包(译者注: 应该是errors包),我们在里面定义了一堆 STDERR 行为,这些行为在不同的子服务之间共享。你不想叫它 errors
,因为如果你这么叫的话,你就得实现所有 errors
包里的通用错误函数。所以我们最后叫它 in-errors
。我不知道,也许这是我现在埋下的一个坑,日后会反咬我一口,但……我觉得这个做法有一定的道理。
Kris Brandow:对。
Ian Lopshire:我觉得如果你要有一个和标准包里常用名字一样的包,至少应该实现那个包里的所有方法和功能。
Kris Brandow:对。这确实是计算机科学中的另一个“命名难”问题。给东西起个好名字真的很难。我觉得说“不要用名词来命名”这个建议太绝对了。这个说法太广泛了。我觉得它需要更具体一点。具体到什么程度我不清楚。但我喜欢用名词来命名包。
Ian Lopshire:我觉得如果命名有任何硬性规则,那它就不会那么难了,对吧?如果我们能制定一套规则让命名变得简单……我觉得不可能有这样的规则。不过我也讨厌长包名。
Kris Brandow:哦,对。对。
Ian Lopshire:我宁愿它短一点,可能不那么明显,也不希望包名太长,比如“这是我的服务器包”。
Kris Brandow:[笑] 对,我觉得问题是“为什么你有一个 server 包?”为什么不是某个包里有一个 server 类型?这不是 HTTP server 包,而是 http.server
和 http.client
,因为这是 HTTP 包。所以我觉得这就是为什么我不太喜欢“我们应该叫它 rate-limiter
而不是 rate
包”的建议。但你可以在这个包里有很多其他东西,不只是限速器。所以……我猜如果这是一个非常通用的名词,也许你需要想一个更具体的名字。或者编一个名字,用其他语言的词来表示那个东西。我过去常常这么做。
Ian Lopshire:我要说的是,千万别随便起一个毫无意义的名字。我们有很多东西叫 catdog
,是根据一个老电视节目命名的。你根本不知道它是干什么的。不要这么做。
Kris Brandow:[笑] 对。如果你想用有趣的主题名字,你得深入挖掘这些名字的背景,或者其他东西。它得和整个项目的主题相关。在我之前的工作中,我们构建了某个东西,出于某种原因我们叫它“矩阵”,所以我用《黑客帝国》里的角色名字给所有不同的服务命名……但这些名字和这些角色在电影里的角色设定是吻合的,所以是有意义的。这是一个整体的命名体系。但如果你想用这样的引用,你必须全力以赴,而且要贯穿整个代码库。
Ian Lopshire:我们有个东西叫 apicard
,像是《星际迷航》里的皮卡德舰长(Picard),因为这个名字以 API 开头,所以还挺合适的。
Kris Brandow:[笑] 对。我觉得 gopls">gopls
这个名字也是这么来的。他们一开始叫 go lsp
,然后说“哦,我们把这些字母重新排列一下。”有时候这种事情就这样发生了。
Ian Lopshire:对,gopls
很清楚它是做什么的。呃……算是吧。我一开始知道它的时候还是 golsp
,所以可能有点令人困惑。
Kris Brandow:对。
Ian Lopshire:它有所有正确的字母。
Kris Brandow:对,你一看就会明白:“哦,原来是这样。”但我对“不要用普通名词来命名包”的说法还不完全信服。这个说法对我来说不够细致,不够微妙。不过再一次,如果这对你有效,并且适用于你的代码库,而且如果你喜欢用字母做前缀,那我觉得可以继续用。只要团队里的每个人都同意这个做法就行。
Ian Lopshire:对,这就像其他任何模式一样。不要只是开始用。如果你要用,就坚持用,并记录下来,告诉所有人都这样做。
Kris Brandow:对。我们还有什么关于“不要用普通名词来命名包”的话题要说的吗?
Ian Lopshire:没有了。你怎么看在包名里使用下划线?
Kris Brandow:呃,不行。 [笑] 我不想在输入包名时还得打一个下划线---
这太麻烦了。
Ian Lopshire:那在文件名里用下划线呢?不是测试文件的那种。
Kris Brandow:那样没问题。
Ian Lopshire:你觉得那样没问题?
Kris Brandow:对。对。这样你可以方便地阅读文件名。我只需要输入一次文件名,而且我不需要在代码里一直写文件名。
Ian Lopshire:包名里能用下划线吗?我甚至都不知道。
Kris Brandow:可以。你不能用连字符,但可以用下划线。至少我很确定可以用下划线。不过对,你不能用连字符,但可以用下划线。不过对,如果你的包名太长,那就有问题了。你得重新考虑这个问题。因为对,你得一直输入那个名字,而输入下划线很麻烦。我不喜欢输入下划线。所以我不希望包名里有下划线。而且这也意味着你的包名很长,导致你的代码行长度也会变得很长,函数名也会变得很长……对。
Ian Lopshire:对,我觉得你说的对,如果你的包名太长,那你可能在做错什么事情,你应该弄清楚是什么地方出了问题……不过对,我就这些想法了。
Kris Brandow:这是“不受欢迎的观点”环节。好吧,Ian,你的“不受欢迎的观点”是什么?
Ian Lopshire:你能先说吗?我还在想。 [笑]
Kris Brandow:你想让这个话题复杂化。好吧,我想我确实是想把它变成一个节目的主题……所以,我终于想明白了,为什么我对 Rust 有点“无感”。我不想说我不喜欢 Rust,我觉得更好的表达方式是“我对 Rust 有点无感”。我终于搞清楚了为什么我对 Rust 有点无感。原因并不是我讨厌 Rust,而是我对 Rust 的推销方式有些不满。这个想法是我在读 Leslie Lamport 的一篇文章时想到的……让我再找一下那段引述,因为我想准确引用它。这篇文章的标题是《计算与状态机》(Computation and State Machines)。我先读一段前言中的内容,然后解释为什么这是我对 Rust 无感的原因。
他写道:“有很长一段时间,我对计算机科学领域中对编程语言的过度强调感到困扰。一个结果是,程序员可以成为 C++ 专家,却写不出符合预期的程序。典型的计算机科学回应是,程序员应该使用正确的编程规范或开发语言,来代替或辅助 C++。而典型的工业界回应则是为程序员提供更好的调试工具。”这里是重点……“工业界典型的回应是提供更好的调试工具,认为我们可以通过让一只猴子在键盘上敲字,并自动找到其代码中的错误来获得优质的程序。”
首先,我觉得这段话特别搞笑。但我认为它也很好地揭示了为什么我不喜欢这个关于类型安全、内存安全以及其他各种安全性的宣传,认为这些安全性会解决我们代码和软件中的问题。这种理论基于对现代软件问题所在的误解。我认为 Rust 及其周围的理念……不是说这门语言本身不好或没用,我认为它是好的、有用的。但我觉得那种“我们需要 Rust,因为 C 不够安全。如果我们有了内存安全的 C,我们的问题就解决了”的论点是有问题的。
当然,有很多人持有更为细致的观点,比如它确实能解决一些内存安全问题,然后还有其他问题需要解决。但 Rust 的大卖点似乎就是通过引入内存安全和 borrow checker(借用检查器),我们将解决一大类软件问题。我认为这掩盖了 Leslie Lamport 指出的那个问题:我们没有从正确的地方出发。如果你没有从正确的出发点开始,那么你只是在不断尝试,最终试图找到一个好的解决方案。正如那句猴子敲键盘的比喻所指,如果你给无数只猴子打字机,它们最终会写出莎士比亚的作品。
我觉得我们对待软件的方式也有点像这样:“只要你不停地敲,总有一天你会做对。”我们可以用一些工具来加速这个过程,让我们知道什么时候明显做错了。我认为我们应该做的是教会软件工程师,或者说整个计算机工程领域的人,如何真正思考。我觉得这是我们遇到的问题,没有人愿意坐下来认真思考他们要解决的问题是什么。我们缺乏使用那些可以帮助我们进行这种思考和记录的语言的训练……主要是数学。我们想要表示的很多东西都可以用简单的数学来表示,但我们却转向编程语言来解决这些问题,这让我想起了那句:“你会用法语来描述一幅印象派画作吗?”我们依赖编程语言来解决问题,但问题的根源在于我们没有编写正确的软件。我们没有写出正确的程序。
我们甚至没有参考框架来判断“正确”意味着什么。我们从来没有坐下来明确说“这就是我们真正想要做的事”。所以当我们试图远离像 C 这样的语言时,我觉得有些人在说“没人应该再写 C 了。”这有点像在说:“英语是一门混乱的语言,有很多奇怪的地方,所以我们应该让所有人都讲世界语,而不再使用英语。”这听起来很荒谬,不仅不切实际,还很可笑,因为英语的问题通常不在于语言的结构本身,而在于我们是否正确地表达了概念。我觉得编程和软件也是一样的:我们是否正确地表达了概念?
这也是我喜欢 Go 的原因之一。如果你真的坐下来仔细思考你在构建什么,怎么构建,然后你记录下这些并通过严谨的开发过程来实现,Go 是一种非常适合简单、轻松表达这些的语言。这种语言不会妨碍你,当你需要一些帮助时,它有一些工具可以用,但你不一定非得用这些工具。而且它让在团队层面上的协作变得更容易。 Go 推广了 go fmt
,这个工具并不像是纠正某人的代码,而是让我们所有人都能更容易地阅读彼此的代码,从而能够更好地协作,共同识别“这是你应该做的事情”和“这是它实际做的事情”之间的明显差异。
所以再次强调,我并不是说 Rust 不该存在。我觉得 Rust 很酷,它拥有的所有这些功能都很新奇。但这种卖点让我对这门语言有些反感,因为它太强调“Rust 会取代 C,我们必须用 Rust 重写一切,因为它解决了什么什么问题”。我认为对于我们这个行业来说,更有意义的是专注于“我们如何真正让人们正确地编写软件?”以及“如何降低软件的成本?”……我认为这两者其实是一回事。因为现在,我们有太多的软件工程师,但却没有相应数量的软件产出。很多时候,我们就像猴子在键盘上乱敲,“哦,我在试着写这个东西”,“哦,编译器生气了,看来我不能这么做。”于是我们增加了做软件的人数,但实际上并没有进行真正的思考。
我认为这也是为什么人们会认为可以用 AI 来取代软件工程师的原因。这种想法认为,如果我们真的像猴子敲键盘那样写代码,那么只要能校正 AI 的输出,它就会比人类更快地敲键盘。但如果你认识到“我们实际上需要更多的思考,而不仅仅是盲目地敲键盘”, 那么你就会明白 AI 是无法做到这些事情的,因为 AI 不能思考。但这就是我对 Rust 有点无感的漫长解释。Rust 仍然是一个很酷的语言,但它的推销方式让我有点反感。
Ian Lopshire:我同意你说的很多观点,但我确实有一些不同的看法。也许 Rust 的宣传是在说它的借用检查器和内存安全功能会拯救我们,必须要有这些功能……但在我看来,我们都是人类,都会犯错,都会设计得不好,或者有时写出糟糕的代码……Rust 的这些内存安全功能只是为了尽量减少这些错误的影响。比如,如果我在写 C 时让索引溢出了,读取了任意内存,那会有很大的后果。但如果我根本做不到那样……我不知道。
Kris Brandow:我觉得这有两个方面。我们遇到的这些问题中,有多少是这种类型的问题?我认为我们遇到的内存损坏问题其实相对较少。即使有内存损坏问题,比如缓冲区溢出,很多时候这些问题都会被修补。而问题在于,人们没有更新他们使用的软件版本,所以他们继续使用那些有漏洞的未修补版本。这是更大问题的一部分。但我也认为我们喜欢把事情看得好像我们有无限的资源,可以无限快地解决问题。但实际上我们不能花费大量的行业精力来开发这些东西,同时还要花费大量精力重新思考和重新设计我们如何教导软件工程师,并重新培训整个行业。我们并没有这么做。
我们并没有转向那种真正推动软件工程师成为深度思考者的方向,因为这是一个非常困难、非常耗费资源的问题。教会一个人如何批判性思考需要大量的精力,这并不是一件简单的事情。而我们没有在做这件事,因为我觉得我们有了这些工具,它们让我们至少可以相对体面地生产软件。因为有了这些工具和保护措施,我们可以有点凑合过去。就像“你必须用 C,而且我们还得找到一种方法教会所有人如何正确使用 C。”
我不觉得这是不可能的事。我不认为大多数软件工程师学会并理解 C 是不可能的。我觉得我们这个行业已经放弃了这种可能性,因为我们有了这些小语言,它们更容易使用,我们认为可以通过添加这些安全性来解决问题,比如“我们只要再增加一种类型的安全性。如果我们添加类型安全,添加内存安全,添加其他什么东西,就能让每个人都能写出好软件。”
所以我觉得我们这两种观点可能有些冲突,但我确实认为,如果我们继续自欺欺人,认为 C 的问题可以通过 Rust 来解决,或者 C 的问题就是 Rust 的问题,那我们就不可能真正解决问题。
这让我想起了我最近常常思考的另一个问题。几年前,我们这个行业曾经大力推动要从代码库中去除那些“有害”的词汇。比如主从(master/slave)、黑名单/白名单等等,大家提交了很多 PR,进行了大量的改动。我们把“master”改成了“main”或“default”,花了很多精力去做这些变化。我们当时的想法是,“使用这些词汇对有色人种,尤其是黑人有害,他们不应该每天看到这些词。”当时我对此有很大的抵触,我觉得“这不是我们应该花时间的地方。”有人会说,“我们得做点什么。我们可以做这件事,也可以做其他事情……”但现在你回头看看这个行业,这些人现在却在全面拥抱 AI。而如果你去看看这些 AI 的数据是如何清理的,训练过程是如何进行的,尤其是对有色人种的压迫和剥削依然在发生,很多人被摧毁了心理健康。这时,我们的行业为什么不再有那种推动力去反抗这些问题呢?我们说“我们去掉这些词,这是解决更大问题的第一步”,但后续行动却没有跟上。
我觉得在不同的背景下,这种情况也发生在其他问题上。我们说,“我们得让软件变得更好。所以让软件变得更好的方法就是改进编程语言。”但问题不在于语言,而在于我们对概念和理解的掌握。而这正是 Rust 没有解决的问题。但不仅仅是 Rust,任何一种宣称“通过改进语言,我们就能写出更好的软件”的语言都在解决错误的问题。
Ian Lopshire:所以你最终的意思是,问题不在于语言,而在于我们自身。
Kris Brandow:没错。
Ian Lopshire:软件工程师本身。我认为我同意这个观点。
Kris Brandow:再次说明,我觉得 Rust 很酷。我认为 Rust 是一门很棒的语言,大家应该使用 Rust。我们需要内存安全,我们需要借用检查器,我也认为类型安全很重要,所有能够帮助我们更好地编写软件的工具都是好的。但我们不能忘记,当今软件的主要问题并不是缺乏内存安全。没有内存安全的情况下,我们也能写出很多系统。没有类型安全的情况下,我们也能写出很多系统。最终,计算机其实……汇编语言没有任何这些东西,但它却是驱动一切的根本。在最终执行的指令中,没有这些保护措施。
所以,即使没有这些功能,也不妨碍我们编写出好软件。因此,我们应该专注于是什么能真正帮助我们编写出好软件。我认为这些工具可以成为其中的一部分,但前提是我们需要真正教会人们如何思考代码,如何思考他们的程序。我认为如果你真的坐下来花时间思考,你会得到非常简洁的解决方案。如果你把这些简洁的解决方案整合起来,最终你会得到一个非常简洁的大型代码库……因为一旦你陷入了代码混乱中,要脱身就会变得更加困难。
Ian Lopshire:没错。
Kris Brandow:我们看看这会有多不受欢迎……不过,我觉得大家可能反而会喜欢,因为我刚才说 Go 是编写优秀软件的理想语言。嗯,也不算是理想吧,但它确实是其中的一个好选择。我可能会说在理论上,Lisp 是更理想的一种语言,尤其是从计算的角度,如果你想严谨地将理论转化为可运行的代码。
Ian Lopshire:我觉得我有一个不太受欢迎的观点。
Kris Brandow:好吧,说来听听。
Ian Lopshire:这是我想做一期节目的话题……我的观点是,学习 Go 并不容易。我知道我们常常说:“哦,Go 很简单,你下午就能学会。”但我想说的是,学习任何新语言都不容易。我们说“学习一门语言”时,很多人以为我们指的是学习这门语言的语法,比如如何写 for 循环,如何定义变量。但我觉得这甚至连冰山一角都不是,只是冰山顶端的一小部分。学习一门语言意味着学习它的编程范式,学习它的标准库是如何运作的,学习如何用这门语言编写惯用的代码……而这些你不可能在一夜之间就掌握。这些都需要通过经验慢慢积累。所以,当我们说一门语言很容易学时,我觉得这是对整个社区的不负责任。
认为你可以马上用一门新语言编写出高质量的代码,我觉得是不现实的。至少对我来说不现实。当你刚开始学一门语言时,你甚至不知道自己写的代码是否是好的。所以,学习任何编程语言都不容易。学习语法是容易的,其他的都很难。
Kris Brandow:是的,我同意。因为我刚才在脑海里把它转化了一下,如果你把它和自然语言对比来看,你可以学一门自然语言的语法和语法规则……但一门语言中还深深嵌入了开发这门语言的社区文化。学习并理解这种文化需要付出大量的努力,尤其是当这种文化与你自己的文化存在冲突或者差异时。我觉得这就像……我有一些东欧的朋友,他们表达事情的方式和美国人表达的方式有很大不同,尤其是在表达兴奋的程度上。我觉得编程语言中也有这种差别。比如,在 Go 和 Rust 中表达某些东西的方式会非常不同,因为它们各自的社区文化非常不同。所以你或许可以学会语法,但要真正理解这些文化差异就非常困难了。
Ian Lopshire:我觉得这是个完美的类比。
Kris Brandow: 是的。我觉得我们的这些不受欢迎的观点有点相关,因为其中的难点在于,如何用一种语言以应该有的方式表达你想要表达的东西?我觉得这就是为什么有些语言或者概念会被某种语言所“垄断”的原因。因为人们会说:“哦,那门语言很容易学。”我觉得 Python 和数据科学就是一个很好的例子。“哦,Python 很容易学,所以你可以用它做数据科学和数据工程”,于是这个领域中的一切几乎都围绕它展开了。但是,做这些事情的人真的理解他们在做什么,并且只是把这些概念映射到这门语言中?还是他们只能在这门语言中表达这些想法,而无法将其移植到其他语言中?
我觉得这其实是我不受欢迎观点的更简洁的表达:我们需要更多地关注概念的理解,并将其映射到语言中,而不是让语言成为我们表达概念的唯一工具。
Ian Lopshire:是的,感觉我们现在的学习方式是从内到外,而不是从外到内。
Kris Brandow:对。我们过于关注语言本身……这也是 Leslie Lamport 的观点。我还没读完他的《计算与状态机》 这篇论文,但在开头他提到了一些有趣的东西。他说:“想象一下,如果我们在物理学中不使用方程。”于是物理学的两个不同领域会有各自的特殊符号,可能不会明显看出 E=MC² 在这两个不同领域中是同一回事。或者换作任何内容也是一样的。如果你使用不同的符号,那么在两个不同领域中识别相似性和模式就会很困难,或者不容易看出你在两个地方其实是在做同样的事情。
他的观点是:“哦,你有方程,虽然两个方程表达的东西一样,但它们在你从事的物理学分支中可能并不意味着同样的事情。”同样的道理也可以应用于自然语言……你可以对两个不同的社区说同样一句英语短语,但它们的含义可能截然不同,尽管词汇是相同的。但由于你有共同的基础词汇,你可以理解并翻译这些不同的含义。
Ian Lopshire:是的,我跟非软件行业的人交流时就有这种问题。我不经意间会用一些工作中的术语,他们就会问:“你在说什么?”
Kris Brandow:对。这就是从初学者到大师的转变。当你能把一个概念解释给完全没有相关背景的人时,你就知道你已经掌握了它。因为你理解了事物的本质,因此能够更好地将其传达给其他人。
所以,我觉得这两个话题都可以做成不错的节目。我可以找一些可能会不同意我的人,邀请 Johnny 来,看看谁还会不同意我……也许我们可以邀请 Sam 回来,和我辩论一下……如果我要做一期关于“我对 Rust 没什么感觉”的节目,我得找一些会不同意我的人。如果他们同意我,那可能就没那么有趣了。或者,可能会很有趣。
Ian Lopshire:你知道 Sam 对 Rust 的看法吗?
Kris Brandow:我不知道……我不能替他发言。我想他对 Rust 应该还好,但我也不太确定。所以这会是个好机会来了解他的想法。
Ian Lopshire:嗯,我也想知道。我对 Rust 还没有完全定下结论……
Kris Brandow:我可能会去学 Rust……我对 Rust 的“无感”并不是因为这门语言本身,或者它的实用性和价值。我理解它的背景和用处。这更是一个宏观层面的问题。大家应该去学习概念,学习如何思考。这才是关键。我们都需要学习如何思考。我希望大家不要觉得这是在侮辱整个行业。我不是在说我们这个行业的人不思考。思考是非常困难的事,而且学习如何深入、批判性地思考也是非常困难的。所以说“我们没有在思考”并不是对任何人的侮辱,而是我们应该改进的地方,如果我们想开发出更好的软件的话。是的,我们必须去思考。这很难,但我们拿了很多钱,所以应该做这份艰难的工作。
好了,我觉得这就是个不错的收尾点了……Ian,你还有什么要补充的吗?
Ian Lopshire:我觉得该说的我们都说了。
Kris Brandow:我也这么觉得。感谢你加入我们的讨论,也感谢听众们收听这一期有些发散的话题。我希望你们和上次一样喜欢这一期节目。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。