当前位置: 首页 > 后端技术 > Python

Go设计理念:少即是多,从何而来?

时间:2023-03-25 21:56:52 Python

大家好,我是炸鱼。之前在围棋社区分享知识和经验的时候,经常听到这样的俚语:lessismore,lessismore,道简单,道无止境。甚至在讨论围棋问题和提案时,有些人会用“lessismore”来反驳或支持论点,这很有趣。大家会很好奇,出处在哪,什么意思?金句的源头是这样一个根深蒂固的社区文化概念,肯定是某个核心人物提出来的。他就是说这句话的作者:有人认得吗?他就是Go语言之父RobPike。RobPike曾多次提到“少即是多”之类的话,并广为流传。分享的场合如:2012年在GoSF大会上,分享了《Less is exponentially more》,这是最早的一篇从舆论中整理出来的文章分享。2015年的dotGo大会以分享《Simplicity is Complicated》为主题,持续灌输文化,分享思想。这种观点的各种变种说法在业界广为流传,形成了围棋界独特的“文化”。当然,从社区反应来看,褒贬不一。演讲内容引用自RobPike的《Less is exponentially more》,正文部分由@MIKESPOOK翻译。我会在这里重新整理、修剪、引用、配图,不再重复造轮子。如下图:背景这是我(以下简称RobPike)在2012年6月旧金山Go大会上的演讲,这是一个私人演讲。我并不是代表Go项目团队的任何人在这里发言,但我想首先感谢团队为Go诞生和发展的一切。另外,我要感谢SanFranciscoGo社区给我这次发言的机会。关于Go最让我惊讶的是几周前有人问我,“自从引入Go以来,最让你惊讶的是什么?”选择的语言,但更多的Go程序员来自Python、Ruby,只有少数来自C++。我们(Ken、Robert和我)本身就是C++程序员,我们设计了新的语言来解决我们编写的软件中的问题。奇怪的是,其他C++程序员似乎不太关心这些问题。为什么开发Go今天我想谈谈是什么促使我们创建Go,以及为什么它不应该让我们感到惊讶。我保证更多地谈论Go而不是C++,即使你不知道C++,你仍然会完全切入主题。答案可以概括为:你认为少即是多,还是少即是少?贝尔实验室的故事这里有一个真实的故事作为比喻。如下:贝尔实验室最初用三个数字来标识:111用于物理研究,127用于计算机科学研究,等等。在80年代初期,一份备忘录如期到达,指出由于我们所知道的研究在不断发展,因此必须添加一个额外的数字来识别我们的工作。因此,我们的中心变成了1127。RonHardin半开玩笑地说,如果我们真的更了解这个世界,我们可以降低个位数,这样127就只有27。当然管理层没有听到这个笑话,或者他们没有听到'不想听,但我认为其中有很多智慧。少即是多。你了解得越多,你就越微妙。请务必牢记这一思路。Go开发C++编译的背景回溯到2007年9月,我正在为一个巨大的GoogleC++程序(你们都用过的那个)做一些琐碎但核心的工作。在那个巨大的分布式集群上编译我花了大约45分钟。C++NewFeatureImprovements收到通知,谷歌聘请的几位在C++标准化委员会工作的人将要发表演讲。他们会告诉我们当时称为C++0x(现在称为C++11)的东西会有哪些改进。在长达一小时的报告中,我们听说已经计划了35个功能。事实上还有更多,但报告中只描述了35个特征。当然,有些功能虽小,但意义重大,足以在报告中提及。还有一些非常微妙和难以理解的,比如:左右值引用(rvaluereferences)。可变参数模板。用户定义的文字。这时我问自己一个问题:C++委员会真的认为C++的问题在于它没有足够的特性吗?当然,在另一个RonHardin中,简化语言所取得的成就远远超过添加功能。当然这有点荒谬,但请牢记这一思路。就在这次C++演讲之前的几个月,我自己进行了实验性语言尝试的演讲,你可以在YouTube上看到它,关于我在80年代开发的玩具并发语言。这种语言叫做Newsqueak,它是Go的前身。我做这份报告是因为Newsqueak中缺少一些想法,我在为谷歌工作时重新考虑了这些想法。我相信他们会让编写服务器端代码变得更容易,Google也会从中受益。我实际上尝试用C++实现这些想法,但失败了。很难将C++控制结构与并发操作联系起来,最终很难看到真正的优势。虽然我承认我从来没有真正习惯过C++,但纯C++仍然使一切看起来都太笨重了。所以我放弃了这个想法。但是那份C++0x报告让我重新思考了这个问题。真正让我烦恼的一件事(我相信Ken和Robert也是如此)是新的C++内存模型具有原子类型。将如此微观的描述性细节集合添加到已经不堪重负的类型系统中,感觉绝对是错误的。这也是短视的,几乎可以肯定硬件将在未来十年内迅速发展,将一种语言与今天的硬件联系得太紧密是愚蠢的。Go初始团队报告后,我们回到办公室。我启动了另一份汇编,将主席交给了罗伯特,并开始就关键问题进行交流。在编译完成之前,我们让Ken参与并决定要做什么。我们不会再编写C++,而且我们——尤其是我——希望能够在编写Google代码时轻松地编写并发。同时,我们也想往前控制“大编程”,这个以后再说。GoFeatureDiscussion我们在白板上写下了一堆想要的东西,以及它们的必要条件。忽略句法和语义细节,设想蓝图和大局。我仍然记得那个时候的电子邮件。以下是摘录:Robert:起点是C,修复了一些明显的错误,消除了混乱,并添加了一些缺失的功能。Rob:将其命名为“go”。名字的由来你可以编造,但它有一个很好的基础。它很短而且很容易拼写。工具:goc、gol、goa。如果有一个交互式调试器/解释器,它可以被称为“go”。扩展名为.go。罗伯特:空接口是接口{}。它们实现了所有接口,因此可以使用this代替void*。我们没有正确地描绘一切。比如画数组和切片,就花了将近一年的时间。但是关于这个语言特性的大部分重要事情都在前几天解决了。请注意,罗伯特说C是起点,而不是C++。我不确定,但我相信他指的是C,尤其是Ken。但事实是,最后我们没有从C开始,我们从头开始,只是借用了运算符、圆括号、大括号和一些关键字。(当然还借鉴了我们知道的其他语言。)无论如何,我们现在用C++做相反的事情,解构一切,从头开始。我们没有尝试设计更好的C++,甚至更好的C。只是为我们关心的软件类型设计一种更好的语言。最后,它变成了一种完全不同于C和C++的语言。每个版本都越来越不同。Go功能列表我列出了Go中对C和C++所做的重要简化:规范语法(不需要符号表进行解析)。垃圾收集(唯一)。没有头文件。没有循环依赖的显式依赖。常量只能是数字。int和int32是不同的类型。字母大小写设置可见性。任何类型都可以有方法(没有类)。没有子类型继承(没有子类)。包级初始化和定义的初始化顺序。文件编译成一个包。包级全局表达式与顺序无关。没有算术转换(辅助常量)。隐式接口实现(不需要“实现”定义)。嵌入(不提升到父类)。方法的定义类似于函数(没有特殊的位置要求)。方法是函数。接口只包含方法(没有数据)。方法仅按名称匹配(不按类型)。没有构造函数或析构函数方法。后自增和后自减是语句,不是表达式。没有预递增或预递减。赋值不是表达式。按照赋值、函数调用定义的顺序执行(无“顺序点”)。没有指针算法。内存总是零初始化的。取局部变量的地址是合法的。方法没有“this”。分段堆栈。没有静态或其他类型的注释。没有模板。没有什么不寻常的。内置字符串、切片、映射。数组边界检查。除了这个简化的列表和一些没有提及的琐碎事项之外,我相信Go比C或C++更具表现力。少即是多。但即使这样也不能把所有东西都扔掉。仍然需要构建类型的工作方式、实践中的正确语法以及让人感觉禁忌的东西,以使库更好地交互。我们还添加了一些C或C++没有的东西,例如切片和映射、复合声明、每个文件的顶级表达式(一个几乎被遗忘的重要东西)、反射、垃圾收集等。当然,还有也是并发。无法想象没有泛型当然,显然缺少的是类型层次结构。请允许我就此说几句。在Go的原始版本中,有人告诉我他无法想象在没有泛型范式的情况下使用一种语言工作。就像之前在某处提到过一样,我认为这是一个绝对令人惊叹的评论。公平地说,他可能正在以他自己的方式表达他是多么喜欢C++中的STL为他所做的事情。在辩论的前提下,我们先相信他的观点。他说编写一个像整数列表或字符串映射这样的容器是一种无法忍受的负担。我认为这是一个了不起的观点。即使在没有泛型范式的语言中,我花在这些问题上的时间也很少。面向对象的方法但更重要的是,他说类型是减轻这些负担的解决方案。类型。不是功能多态性,不是语言基础,也不是其他帮助,只是类型。这是困扰我的细节问题。从C++和Java转到Go的程序员怀念编程与类型一起工作的方式,尤其是继承和子类化,以及所有这些东西。说到类型,也许我是个门外汉,但我真的从来没有发现这个模型非常具有表现力。我已故的朋友AlainFournier曾经告诉我,他认为学术的最低形式是分类。所以你知道吗?类型层次结构是分类。您必须决定将什么放入哪个框,包括每种类型的父级,是A继承自B,还是B继承自A。可排序数组是排序数组还是由数组表示的排序器?如果你坚信所有的问题都是类型驱动设计,那么你就必须做出决定。我认为以这种方式思考编程是荒谬的。核心不是事物之间的祖传关系,而是它们能为你做什么。当然,这就是接口进入Go的地方。但它们已经是蓝图的一部分,这才是真正的Go哲学。如果说C++和Java是关于类型继承和类型分类的,那么Go就是关于组合的。Unix管道的最终发明者道格·麦克罗伊(DougMcIlroy)在1964年写道(!):我们应该以某种方式逐段连接消息数据,就像花园的龙头和软管一样。这也是IO使用的方法。这也是Go使用的方法。Go采纳了这个想法,并将其向前迈进了一大步。这是一种关于组合和连接的语言。一个明显的例子是接口为我们提供组合组件的方式。只要它实现了方法M,它就可以到位,不管它是什么。另一个重要的例子是并发如何连接独立运行的计算。还有一种不寻常(但非常简单)的类型组合模式:嵌入。这是Go特有的组合技术,与C++或Java程序有着完全不同的风味。C++/Java的大编程模式有一个不相关的Go设计,我想提一下:Go旨在帮助编写大型程序,由大型团队编写和维护。有一种叫做“大编程”的想法,不知何故C++和Java已经开始主导这个领域。我认为这只是一个历史失误,或者说是工业化的意外。但是一个被广泛接受的信念是面向对象设计可以做事。我根本不相信。大软件确实需要方法论的保驾护航,但不需要那么强的依赖管理,那么清晰的接口抽象,甚至不需要那么华丽的文档工具,但并不比强大的依赖管理,清晰的接口抽象,优秀的文档更重要工具,而这些都不是C++擅长的事情(尽管Java显然做得更好)。我们还不知道,因为用Go编写的软件还不够多,但我相信Go会在大型编程世界中脱颖而出。时间证明一切。为什么Go不被C++程序员喜欢现在,回到我在演讲开始时提出的那个惊人的问题:为什么Go这种旨在摧毁C++的语言并没有赢得C++程序员的心。?撇开玩笑不谈,我认为这是因为Go和C++有着完全不同的哲学。C++就是关于触手可及的解决方案。我引用C++11常见问题解答中的这段话:C++具有比大量增加的临时手写代码更广泛的抽象、优雅、灵活和零成本的表达能力。这个思路和Go的不一样。零成本不是目标,至少不是零CPU成本。Go的主张更多是关于最小化程序员的工作量。去不是一切。您无法将所有内容都内置。您无法精确控制每个微小的实现。例如,没有RAII。相反,可以使用垃圾收集。也没有内存释放功能。您得到的东西功能强大,但易于理解,易于使用来构建连接和组合以解决问题的模块。这可能最终不会像您用另一种语言编写的解决方案那样快速、优雅或在思想上清晰,但它肯定会更容易编写、更容易阅读、更容易理解、更容易维护和更安全.换句话说,当然有点过于简单了:Python和Ruby程序员:GotoGo,因为他们没有放弃太多的表达能力,而是获得了性能并与并发性共舞。C++程序员:不能转移到Go,因为他们努力争取对他们的语言的精确控制,并且不想放弃他们已经获得的任何东西。对他们来说,软件不仅仅是完成工作,而是以定义的方式完成工作。那么问题来了,围棋的成功是否反驳了他们的世界观?我们应该从一开始就意识到这一点。对C++11的新特性感到兴奋的人不会关心没有这么多特性的语言。即使事实证明这种语言可以提供的东西比他们想象的要多。谢谢你们。总结一直很好奇围棋的哲学“lessismore”,出处在哪里,含义是什么?我在春节期间阅读并整理了它。虽然演讲的内容比较多,但也比较口语化。但本质上RobPike的“lessismore”是一个更有趣的东西。核心观点是:“Go与C++有着完全不同的概念,希望将程序员的工作量降到最低,其自身的少量特征应该能够连接组合解决问题,表现力更强,而不是堆函数。”你怎么认为?:)如有任何问题,欢迎在评论区反馈交流。最好的关系是相互成就。您的好评是创作炸鱼最大的动力。感谢您的支持。文章持续更新中。可以微信搜索【脑补炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。推荐阅读为什么Go在语言层面不支持map并发?Go泛型的3大核心设计你学会了吗?