当前位置 : 主页 > 编程语言 > 其它开发 >

人物:Rob Pike

来源:互联网 收集:自由互联 发布时间:2022-05-30
近几年来在计算机语言排行榜上成长最快的语言就是 Go 语言。它勇往直前势如破竹,从几年前的 50 名之外快速跃进前十的宝座,而且还在马不停蹄向上攀登。 大家都知道 Go 语言是 G

近几年来在计算机语言排行榜上成长最快的语言就是 Go 语言。它勇往直前势如破竹,从几年前的 50 名之外快速跃进前十的宝座,而且还在马不停蹄向上攀登。

大家都知道 Go 语言是 Google 推出来的,它是由 Google 包养了一群骨灰级程序员组成了一个叫着「Go Team」的精英团队共同打造。这个队伍里有鼎鼎大名的 Unix 操作系统发明人 Ken Thompson,大胡子形象的他在人群之中拥有极高的辨识度。Ken Thompson 今年已经 75 岁了,刚参与 Go 项目时他正处于个人职业生涯的半退休状态,头发几乎全部掉光。

     这个队伍里还有另外一个其貌不扬头发不多的灵魂人物,从 Go 团队建立之初,他就一直在里面了。这个人名气没有 Ken Thompson 那么大,知道他的人并不是太多,而这个人其实才是 Go 语言「元团队」里最为核心的人物 ,他就是今天要讲的程序英雄 —— 加拿大人 Rob Pike。  

 

 

 

我们现在看到的他已经是一个 60 多岁的老头了。


 他来 Google 之前一直在贝尔实验室捣鼓它的 Plan9 操作系统,虽然几乎没人听说过世界上还有这么一个操作系统,Plan9 这个项目已经活了很多年,到现在还有不少人在继续维护,Plan9 的官方主页是 https://9p.io/plan9/。我翻阅了官网上对 Plan9 的说明文档,里面有关 Plan9 的论文多达几十篇,这些论文中 Rob Pike 的名字署满了近一半。这足以说明 Rob Pike 不容小嘘,他就是 Plan9 操作系统的灵魂人物。   他是一个全能型程序员,从操作系统写到编译器,又从文本编辑器写到了 UI 界面。看到 Rob Pike 的伟大功绩。

我本以为他只是一个单纯的程序员,但是查阅资料后发现,他居然还是一位业余天文学家,发明过珈玛射线射电望远镜。

  更要命的是他还参加了 1980 年的奥运会射箭项目夺得了银牌。    

1990年他上了电视,参与了大卫莱特曼的电视节目《Late Night with David Letterman》。

  写过两本书,分别是《Unix 编程环境》和《程序编程实践》,感兴趣的同学可以买来读一读,字字珠玑,读后必定受益匪浅。

 

他说过一句话让我等数据结构学的很一般的后辈特别受鼓舞 —— 只要掌握了数据结构中的四大法宝,就可以包打天下,他们是:array 、linked list 、hash table、binary tree 。这四大法宝可不是各自为战的,灵活结合才能游刃有余。比如,一个用 hash table 组织的 symbol table,其中个个都是由字符型 array 构成的 linked list 组成的。

 

他和 Linus 大神有相似的观点 —— 以数据为中心。如果已经选择了正确的数据结构并且把一切都组织得井井有条,正确的算法也就不言自明。编程的核心是数据结构,而不是算法。

如果你喜欢玩弄高级的算法和数据结构,他说过的话可能会打击你 —— 花哨的算法比简单算法更容易出 bug 、更难实现。尽量使用简单的算法配合简单的数据结构。

 

在他心目中,他一直以 Ken Thompson 为自己的导师

在我加入贝尔实验室一年多后,我开始和 Ken Thompson 一起在开发一个针对由 Gerard Holzmann 设计的很小的图形化交换语言的即时编译器上做结对编程。我打字比较快,所以我坐在电脑前,Ken 站在我身后看我编程。我们开发的很快,但经常会遇到问题,而且可以看出来出错了 —— 毕竟这是一个图形化的编程语言。当程序出错时,我本能的一头扎进问题,检查报错跟踪信息,添加调试打印语句,启动调试器,等等,但 Ken 只是站在那思考,完全不理会我也不查看我们写的出问题的代码。

 

一段时间后我发现一个规律,Ken 经常会比我先找到问题出在什么地方,而且会突然的喊一嗓子,“我知道什么地方的问题了。”每次他的判断都很准确。我认识到,Ken 已经在脑海里构建了代码的模型,当有问题出现时,那是他脑子里的模型出了问题。在思考为什么会发生这些错误时,他能凭直觉找到模型中什么地方不对或发现写的代码跟这个模式什么地方有出入

Ken 教会了我一个极其重要的习惯:纠错前先思考。如果你一头扎进问题中,你可能只解决了当前出现问题的代码,但如果你先思考这个错误,这个 bug 是怎么引入的?你通常发现和纠正一个更高层次的问题,进而改进了系统设计,防止了更多 bug 的出现。

我认识到这种编程思考模式非常的重要。有些人痴迷于一行行的、使用各种工具来调试所有的东西。但我现在相信,思考 —— 不看代码的思考 —— 是最好的调试途径,因为它能让你开发出更好的软件。

 

他和 Ken Thompson 一起发明了全世界无猿不知的 UTF8 编码格式。在前沿科技的量子计算和通讯领域也做过一些深入研究。

 

Rob提到:Go语言本是以C为原型,以C++为目标设计,但最终却大相径庭。值得一提的是,这3门语言都曾当选TIOBE年度语言。

 

几个礼拜之前我被问到:“对于Go语言,最令你惊讶的是什么?”当时我就明确地给出了答案:“虽然我希望C++程序员能够使用Go作为替代拼,但实际上大部分Go程序员都是从Python和Ruby转过来的,其中却少有C++程序员。

我、Ken以及Robert都曾是C++程序员,在我们编写软件时觉得应该设计一门更适合解决这个问题的编程语言。奇怪的是,其他程序员似乎却不关心。

 

今天我将说说是什么让我们决定创造Go语言的,及其出乎意料的结果。这里我谈的更多的会是Go而不是C++,所以即使你不懂C++也没关系。

 

主旨可以简单地总结为:你更同意Less ismore还是Less isless?

 

这里有一个真实的故事。贝尔实验室中心本来分配3位数字作代号:物理搜索是111,计算科学搜索是127,以及等等。20世纪80年代,随着对搜索的进一步理解,我们认为有必要新增数位以更好地特征化工作,于是我们的中心就成了1127。

Ron Hardin半开玩笑半正经地说如果我们真的更好地理解了世界,我们已经可以去掉一个数位,把127变成27。当然,管理人员并没有相信这个玩笑,当然Ron也没指望他们会相信,但我认为这很有哲理。有时候少意味着更多,你理解得越深,就能越干练。

 

回想2007年9月的时候,我正在为Google庞大的C++程序做一些比较琐碎但是核心的工作(对,就是你们都用过的那个!),我工作在Google庞大的分布式编译集群上都需要花45分钟。之后有几个C++标准委员会的Google员工为我们做了个演讲,他们给我们介绍了下C++0x(现在被叫做C++11)里会有什么新东西。

 

在那一个小时的演讲中,我们大概听到了约35种计划中的新特性。当然实际上还有更多,但是在那次演讲中我们只听到了35种左右。有的特性比较小,当然演讲中所提到的任何一种特性都可以称得上标志性的:有的比较精妙,但是很难理解,像右值引用(RValue Reference);其它的很有C++的特色,例如variadic模板;还有一些只是一些相当疯狂的,一如用户定义文字(user-definedliteral)。

 

这时候,我问了自己一个问题:C++委员会真的认为C++的特性还不够多?当然,不同于Ron的玩笑,简化这门语言必是一门更大的成就!也许这很可笑,但是请把这个想法记在心里。

 

在那个C++演讲的几个月之前,我做了一个演讲(你可以在YouTube上看到),是关于一个我在上世纪80年代开发的玩具性的并发性语言。那个语言叫作Newsqueak,也就是Go的前身。

 

我做那个演讲是因为我在Google工作的时候忘记了一些Newsqueak里的好想法,现在回顾一下。我确信它能简化服务器端代码编写,而且对Google也有好处。

 

实际上我也试过把这个想法加入C++,但是失败了。把并发操作和C++控制结构整合到一起非常困难,反过来也意味着即使侥幸成功收益也会非常小。

 

不过C++的演讲让我又重新思考了一遍。新的C++内存模型使用了原子类型,这让我很困扰,我认为Ken和Robert肯定也不喜欢。感觉把这样一个如此细节的功能加入已经超负载的类型系统里并不是一个明智的选择。这看起来也很目光短浅,因为硬件也可能在接下来的十年里发生明显的变化,所以把语言和当今的硬件捆绑地太紧不见得是一个正确的选择。

 

演讲结束后,我们又回到了办公室。我又开始了编译工作,我转过椅子好面向Robert,然后开始问一些关键性的问题。在编译结束之前,我们说服了Ken,决定一起做一些事情。我们再也不想用C++了,同时也很希望能在写Google的代码时能用到并行性特性。“大代码”问题也是一个需要关注的问题,但我们最终决定这个问题最后再解决。

 

然后我们找了块白板,在上面写下希望能有哪些功能。这里我们只关注大的方面,语法和语义学这些细节性的东西先放到一边去。

之后我们在邮件上交流,这里是内容摘选:

Robert:以C语言为原型,修补部分明显的缺陷,去掉垃圾功能,添加一些缺失的功能。

Rob:命名为“Go”,好处有很多,这名字非常简短,容易拼写。工具可以叫做:goc、gol、goa。如果有可交互的调试器/解释器也可以简单地叫它“Go”,后缀名用 .go。

Robert:定义空接口语法:interface{}。所有接口都通过这个实现,因此可以用它代替void*。

 

我们没有一开始就设计出所有的功能,之后花了一年的时间才确定了array和slice功能,不过也有很多语言的特色在最初几天就已经确定下来的。

 

注意:Robert说要以C语言为原型,而不是C++!但实际上我们也并不是真的从C开始,只是从中借了部分内容,比如运算符、括号和几个相同的关键字。当然为了让它成为最适合我们的语言,我们从所有了解的语言里都借取了一些特性。

 

最后结果毫无疑问跟C或C++大不相同。以下是Go从C和C++简化的功能:

·        规范的语法(不需要符号表来解析)

·        垃圾回收(独有)

·        无头文件

·        明确的依赖

·        无循环依赖

·        常量只能是数字

·        int和int32是两种类型

·        字母大小写设置可见性(letter case sets visibility)

·        任何类型(type)都有方法(不是类型)

·        没有子类型继承(不是子类)

·        包级别初始化以及明确的初始化顺序

·        文件被编译到一个包里

·        包package-levelglobals presented in any order

·        没有数值类型转换(常量起辅助作用)

·        接口隐式实现(没有“implement”声明)

·        嵌入(不会提升到超类)

·        方法按照函数声明(没有特别的位置要求)

·        方法即函数

·        接口只有方法(没有数据)

·        方法通过名字匹配(而非类型)

·        没有构造函数和析构函数

·        postincrement(如++i)是状态,不是表达式

·        没有preincrement(i++)和predecrement

·        赋值不是表达式

·        明确赋值和函数调用中的计算顺序(没有“sequence point”)

·        没有指针运算

·        内存一直以零值初始化

·        局部变量取值合法

·        方法中没有“this”

·        分段的堆栈

·        没有静态和其它类型的注释

·        没有模板

·        没有异常

·        内建string、slice和map

·        数组边界检查

 

因为有这么多功能的简化,我相信Go比C和C++更有表现力。Less canbe more!

 

但是也不能全部丢弃。你需要把想法分为模块,例如:类型如何工作、能够实际上良好运行的语法以及有助于保证类库交互的东西。

 

我们也加入了一下C和C++里没有的东西,比如slice和map、复合文本(composite literal)、顶层文件表达式(这是一个大问题,但经常被忽视)、映射(reflection)、垃圾回收等等,我们还非常自然地加入了并发性。

很明显还缺少了类型层次结构,这让我很生气。

 

在Go语言的首次启航中,我得到了一个“我不能在没有泛型的环境下工作”的反馈,这真是一个奇怪的言论。

 

平心而论,我觉得他可能是因为在C++里用惯了STL的原因。从字面意思来看,这就是说着写一个int list或者stringmap对他来说是一个非常大的负担。我不会在这种奇怪的问题上花太长的时间,所以这种需要泛型支持的也一样。

但更重要的是,只有类型是用来解决这样的问题的,既不是多态函数(polymorphic function)也不是语言原语(language primitive)或者其它类型的帮助,只有类型!

 

这就是卡住我的的细节。

 

从C++和Java转向Go的程序员很怀念使用类型编程的方式,特别是继承和子类这样的概念。也许关于“类型”我只是一个门外汉,但我真没发现模板有什么好处。

 

我已故的朋友Alain Fournier曾告诉我,他认为学术工作最基本的要求就是分工。你知道吗?类型层次也只是一种分类法。你需要决定什么东西封装在什么里面,每个对象的父类型是什么?到底是A继承B还是B继承A?可排序数组究竟是用来排序的数组还是一个通过数组实现的排序器?如果你坚信类型决定设计那这个就是必须理清的问题!

 

我认为这是一个对于编程的荒谬的思考方式。真正重要的是这些类能为你做什么而不是它们之间是什么关系!

 

当然这就是Go语言里接口的关系,不过这只是部分规则,Go语言的设计理念实际上比这还要复杂得多。

 

如果说C++和Java是关于类的层次和分类,那么Go的核心思想就是组合(composition)。

 

Doug McIlroy,Unix管道原理的最终发明者,在1964年写道:

 

当我们需要将数据以另一种方式整合到一起时,我们需要一些类似花园里软管的方法将程序耦合在一起,这也和IO的处理方式一样。

同样Go也是这样——Go取自这一思想,但是更进一步——它就是一个组合与联结的语言。

 

举个例子:接口提供了组合的方式,只要实现了方法M就可以直接放在那儿,无论它是什么都会很合适。

另一个例子是关于并发性如何给我们带来独立执行计算能力。

 

Go有一个非常不同寻常但是很简单的类型组成方式:嵌入。这些组合技术正是Go的独特之处,与C++或者Java程序截然不同。


虽然这与Go语言的设计不是很相关,但我还是想说:Go是为大型程序设计的,需要大的团队编写和维护。

大型程序设计,通常被认为是C++和Java的领域。我认为,这只是一个历史遗留问题,或许还是一个产业事故,但普遍被认为这和面向对象设计有关。

 

我不赞同这个观点,大软件需要确定的理念,但强依赖管理、整洁的接口、抽象化以及优秀的文档工具这些更为重要!不幸的是,这些没一个是C++的强项(不过这点Java明显就好得多)。

当然,现在还不能确定,因为用Go语言编写的程序还不够多,但我相信Go会成为一个非常好的大型程序编程语言。时间会证明一切!

 


现在,回到我们讲话的中心:

 

为什么Go从头到尾都是以C++为目标来设计的却无法吸引到C++程序员?

 

Joke认为,这可能是因为Go和C++的设计理念相差甚大吧。

 

C++希望所有解决方案都能很容易得到/使用。我在C++11 FAQ(常见问题)上看到了这句话:

 

  C++能够优美地、灵活地并且零成本地表现出抽象事物的能力,相比专门编写的代码要高效很多。

 

这种思考方式和Go的运转方式并不一样。零成本不是目标,至少零CPU成本不是。Go的目标是解放程序员!

 

Go并没有包罗万象,你不要期待它什么都有,你也无法精确地控制每一个细节。例如:Go没有RAII,但是你可以使用一个垃圾清理器做替代,虽然你甚至无法使用内存释放函数。

你可以从Go中得到很多易于理解但是强大的工具集来组合出问题的解决方案。它跟别的语言比起来也许没有那么快或者精致,但肯定写起来更简单、读起来更容易,也更易于理解,也许还更安全。

从另一个角度看,Go肯定更加简化:

 

Python和Ruby程序员转向Go是因为他们不需要学更多的关心却可以获得更好的性能,甚至还可以使用并发特性。

 

C++程序员不愿意转向Go是因为他们竭尽全力只为了对自己的程序的完全掌控,并不希望这样改变。对于他们,软件并不仅是完成工作,而是做好工作。

现在的问题在于,如果Go成功了,会颠覆他们的世界观。

 

而且我们应该在一开始就注意到:对C++11新特性感兴趣的人是不会对一个功能如此简洁的语言感兴趣的,即使它能完成更多的任务。

   
上一篇:基于Android实现K歌场景下超低延时耳返
下一篇:没有了
网友评论