Less is exponentially more

Less is exponentially more
Author: Rob Pike

原文 Less is exponentially more 是 Rob Pike 自己整理的他在六月22日,旧金山的 Golang 会议上的演讲稿。清晰的介绍了 Go 的前世今生,来龙去脉。为了让更多的人能够更加清楚的认识到 Go 的优雅并喜爱上 Go,特翻译成中文,以飧读者。

Here is the text of the talk I gave at the Go SF meeting in June, 2012.
这是我(Rob Pike)在 2012 年六月,旧金山 Go 会议上的演讲内容。

This is a personal talk. I do not speak for anyone else on the Go team here, although I want to acknowledge right up front that the team is what made and continues to make Go happen. I’d also like to thank the Go SF organizers for giving me the opportunity to talk to you.
这是一个私人演讲。我并未代表 Go 项目团队的任何人在此演讲,但我首先要感谢团队为 Go 的诞生和发展所做的一切。同时,我也要感谢旧金山 Go 社区给我这个演讲机会。

I was asked a few weeks ago, “What was the biggest surprise you encountered rolling out Go?” I knew the answer instantly: Although we expected C++ programmers to see Go as an alternative, instead most Go programmers come from languages like Python and Ruby. Very few come from C++.
在几个星期之前我被问到,“在推出 Go 之后,什么令你感到最为惊奇?”我立刻有了一个答案:尽管我们希望 C++ 程序员来了解 Go 并作为一个可选的语言,但是更多的 Go 程序员来自如于 Python、Ruby。只有很少来自 C++。

We—Ken, Robert and myself—were C++ programmers when we designed a new language to solve the problems that we thought needed to be solved for the kind of software we wrote. It seems almost paradoxical that other C++ programmers don’t seem to care.
我们——Ken,Robert 和我自己曾经是 C++ 程序员,我们设计新的语言是为了解决那些我们编写的软件中遇到的问题。而这些问题,其他 C++ 程序员似乎并不怎么在意,这看起来有些矛盾。

I’d like to talk today about what prompted us to create Go, and why the result should not have surprised us like this. I promise this will be more about Go than about C++, and that if you don’t know C++ you’ll be able to follow along.
今天我想要谈谈是什么促使我们创建了 Go,以及为什么本不应该是这样的结果会我们惊讶。我承诺讨论 Go 会比讨论 C++ 多,即便你不了解 C++ 也仍然完全跟得上主题。

The answer can be summarized like this: Do you think less is more, or less is less?

Here is a metaphor, in the form of a true story. Bell Labs centers were originally assigned three-letter numbers: 111 for Physics Research, 127 for Computing Sciences Research, and so on. In the early 1980s a memo came around announcing that as our understanding of research had grown, it had become necessary to add another digit so we could better characterize our work. So our center became 1127. Ron Hardin joked, half-seriously, that if we really understood our world better, we could drop a digit and go down from 127 to just 27. Of course management didn’t get the joke, nor were they expected to, but I think there’s wisdom in it. Less can be more. The better you understand, the pithier you can be.
这里有一个真实的故事作为隐喻。贝尔实验室最初用三个数字标识:111 表示物理研究,127 表示计算机科学研究,等等。在上世纪八十年代早期,一篇如期而至的备忘录声明由于我们所了解的研究正在增长,为了便于识别我们的工作,必须再添加一位数。因此,我们的中心变为 1127。Ron Hardin 开玩笑半认真的说道,如果我们真的更好的了解了这个世界,我们可以减少一位数,使得 127 仅为 27。当然管理层没有听到这个笑话,又或者他们不愿意听到,但是我想这其中确有大的智慧。少既是多。你理解得越好,越含蓄。

Keep that idea in mind.

Back around September 2007, I was doing some minor but central work on an enormous Google C++ program, one you’ve all interacted with, and my compilations were taking about 45 minutes on our huge distributed compile cluster. An announcement came around that there was going to be a talk presented by a couple of Google employees serving on the C++ standards committee. They were going to tell us what was coming in C++0x, as it was called at the time. (It’s now known as C++11).
回到 2007 年 9 月,我在一个巨大的 Google C++ 程序(就是你们都用过的那个)上做一些琐碎但是很核心的工作,我在那个巨大的分布式集群上需要花大约 45 分钟进行编译。这时候我收到一个通知说Google公司里面几个为 C++ 标准化委员会工作的雇员将会做一场报告。他们将向我们介绍那时还被称作 C++0x(就是现在众所周知的 C++11)中将会有哪些改进。

In the span of an hour at that talk we heard about something like 35 new features that were being planned. In fact there were many more, but only 35 were described in the talk. Some of the features were minor, of course, but the ones in the talk were at least significant enough to call out. Some were very subtle and hard to understand, like rvalue references, while others are especially C++-like, such as variadic templates, and some others are just crazy, like user-defined literals.
在长达一个小时的报告中,我们听说了诸如有已经在计划中的 35 个特性之类的事情。事实上有更多,但仅有 35 个特性在报告中进行了描述。当然一些特性很小,但是意义重大,值得在报告中提出。一些非常微妙和难以理解,如左右值引用(rvalue references),还有一些是 C++ 特有的,如可变参数模板(variadic templates),还有一些就是发疯,如用户定义数据标识(user-defined literals)。

At this point I asked myself a question: Did the C++ committee really believe that was wrong with C++ was that it didn’t have enough features? Surely, in a variant of Ron Hardin’s joke, it would be a greater achievement to simplify the language rather than to add to it. Of course, that’s ridiculous, but keep the idea in mind.
这时我问了自己一个问题:C++ 委员会真得相信 C++ 的问题在于没有足够的特性?肯定的说,在另一个 Ron Hardin 的玩笑中,简化语言的成就远远大于添加功能。当然这有点可笑,不过请务必记住这个思路。

Just a few months before that C++ talk I had given a talk myself, which you can see on YouTube, about a toy concurrent language I had built way back in the 1980s. That language was called Newsqueak and of course it is a precursor to Go.
就在这个C++报告的数月前,我自己也进行了一个演讲,你可以在 YouTube 上看到,关于我在上世纪 80 年代开发的一个玩具性质的并发语言。这个语言被叫做 Newsqueak,它是 Go 的前辈了。

I gave that talk because there were ideas in Newsqueak that I missed in my work at Google and I had been thinking about them again. I was convinced they would make it easier to write server code and Google could really benefit from that.
我进行这次报告是因为在 Newsqueak 中缺失的一些想法,在为 Google 工作的时候我再次思考了这些它们。我当时确信它们可以使得编写服务端代码变得更加轻松,使得 Google 能从中获得收益。

I actually tried and failed to find a way to bring the ideas to C++. It was too difficult to couple the concurrent operations with C++’s control structures, and in turn that made it too hard to see the real advantages. Plus C++ just made it all seem too cumbersome, although I admit I was never truly facile in the language. So I abandoned the idea.
事实上我曾尝试在 C++ 中实现这些思路,但是失败了。要将 C++ 控制结构和并发操作联系起来太困难了,最终这导致很难看到真正的优势。虽然我承认我从未真正熟练的使用 C++,但是纯粹的 C++ 仍然让所有事情看起来过于笨重。所以我放弃了这个想法。

But the C++0x talk got me thinking again. One thing that really bothered me—and I think Ken and Robert as well—was the new C++ memory model with atomic types. It just felt wrong to put such a microscopically-defined set of details into an already over-burdened type system. It also seemed short-sighted, since it’s likely that hardware will change significantly in the next decade and it would be unwise to couple the language too tightly to today’s hardware.
但是那场 C++0x 报告让我再次思考这个问题。有一件令我十分困扰的事情(同时我相信也在困扰着 Ken 和 Robert)是新的 C++ 内存模型有原子类型。感觉上在一个已经负担过重的类型系统上加入如此微观的描述细节的集合是绝对的错误。这同样是目光短浅的,几乎能确信硬件在接下来的十年中将迅速发展,将语言和当今的硬件结合的过于紧密是不明智的。

We returned to our offices after the talk. I started another compilation, turned my chair around to face Robert, and started asking pointed questions. Before the compilation was done, we’d roped Ken in and had decided to do something. We did not want to be writing in C++ forever, and we—me especially—wanted to have concurrency at my fingertips when writing Google code. We also wanted to address the problem of “programming in the large” head on, about which more later.
在报告后我们回到了办公室。我启动了另一个编译,将椅子转向 Robert,然后开始沟通关键的问题。在编译结束前,我们已经把 Ken 拉了进来,并且决定做些什么。我们不准备继续写 C++ 了,并且我们——尤其是我,希望在写 Google 代码的时候能够轻松的编写并发程序。同时我们也想勇往直前的驾驭“大编程”,后面会谈到。

We wrote on the white board a bunch of stuff that we wanted, desiderata if you will. We thought big, ignoring detailed syntax and semantics and focusing on the big picture.

I still have a fascinating mail thread from that week. Here are a couple of excerpts:

Robert: Starting point: C, fix some obvious flaws, remove crud, add a few missing features.
Robert: 起点:C,修复一些明显的缺陷,移除杂物,添加一些缺失的特性。

Rob: name: ‘go’. you can invent reasons for this name but it has nice properties. it’s short, easy to type. tools: goc, gol, goa. if there’s an interactive debugger/interpreter it could just be called ‘go’. the suffix is .go.
Rob: 命名:“go”。你们可以编造这个名字的来由,不过它有很好的底子。它很短,容易拼写。工具:goc, gol, goa。如果有交互式调试器/解释器,可以就叫做“go”。扩展名是 .go。

Robert Empty interfaces: interface {}. These are implemented by all interfaces, and thus this could take the place of void*.
Robert 空接口:interface {}。它们实现了所有的接口,所以这个可以用来代替 void *。

We didn’t figure it all out right away. For instance, it took us over a year to figure out arrays and slices. But a significant amount of the flavor of the language emerged in that first couple of days.
我们并没有正确描绘全部的东西。例如,为了描绘 array 和 slice 用了我们差不多一年的时间。但是这个语言特色的大多数重要的东西都在开始的几天里确定下来。

Notice that Robert said C was the starting point, not C++. I’m not certain but I believe he meant C proper, especially because Ken was there. But it’s also true that, in the end, we didn’t really start from C. We built from scratch, borrowing only minor things like operators and brace brackets and a few common keywords. (And of course we also borrowed ideas from other languages we knew.) In any case, I see now that we reacted to C++ by going back down to basics, breaking it all down and starting over. We weren’t trying to design a better C++, or even a better C. It was to be a better language overall for the kind of software we cared about.
注意 Robert 说 C 是起点,而不是 C++。我不确定,不过我相信他是指 C,尤其是 Ken 在的情况下。不过事实是,最终我们没有从 C 作为起点。我们从头开始,仅仅借鉴了如运算符、括号、大括号、和部分关键字。(当然也从我们知道的其他语言中吸取了精髓。) 无论如何,我们对 C++ 作出的回应是解构全部,回到原点重新开始。我们并未尝试去设计一个更好的 C++,甚至更好的 C。仅仅是一个对于我们在意的那种类型的软件来说更好的语言。

In the end of course it came out quite different from either C or C++. More different even than many realize. I made a list of significant simplifications in Go over C and C++:
最终,它成为了一个与 C 和 C++ 完全不同的语言。每个发布版本都越来越不同。我制作了一个 Go 中对 C 和 C++ 进行的重要简化的清单:

regular syntax (don’t need a symbol table to parse)规范的语法(无需用于解析的符号表)
garbage collection (only) 垃圾收集(唯一)
no header files 没有头文件
explicit dependencies 显式依赖
no circular dependencies 无循环依赖
constants are just numbers 常量只能为数字
int and int32 are distinct types int 和 int32 是不同的类型
letter case sets visibility 字母大小写设定可见性
methods for any type (no classes) 任何类型都可以有方法(没有类)
no subtype inheritance (no subclasses) 没有子类型继承(没有子类)
package-level initialization and well-defined order of initialization 包级别初始化和定义好的初始化顺序
files compiled together in a package 文件编译到一个包中
package-level globals presented in any order 包级别的全局表达与顺序无关
no arithmetic conversions (constants help) 没有算数转换(常量做了辅助处理)
interfaces are implicit (no “implements” declaration) 隐式的接口实现(无需“implements”定义)
embedding (no promotion to superclass) 嵌入(没有向父类的升级)
methods are declared as functions (no special location) 方法如同函数一样进行定义(没有特的别位置要求)
methods are just functions 方法就是函数
interfaces are just methods (no data) 接口仅仅包含方法(没有数据)
methods match by name only (not by type) 方法仅通过名字匹配(而不是通过类型)
no constructors or destructors 没有构造或者析构方法
postincrement and postincrement are statements, not expressions 后自增和后自减是语句,而不是表达式
no preincrement or predecrement 没有前自增或前自减
assignment is not an expression 赋值不是表达式
evaluation order defined in assignment, function call (no “sequence point”) 按照赋值、函数调用定义时的顺序执行(没有“sequence point”)
no pointer arithmetic 没有指针运算
memory is always zeroed 内存总是零值初始化
legal to take address of local variable 对局部变量取地址合法
no “this” in methods 方法没有“this”
segmented stacks 分段的堆栈
no const or other type annotations 没有静态或其他类型注解
no templates 没有模板
no exceptions 没有异常
builtin string, slice, map 内建 string、slice、map
array bounds checking 数组边界检查
And yet, with that long list of simplifications and missing pieces, Go is, I believe, more expressive than C or C++. Less can be more.
除了这个简化清单和一些未提及的琐碎内容,我相信,Go 相比 C 或者 C++ 是更加有表达力的。少既是多。

But you can’t take out everything. You need building blocks such as an idea about how types behave, and syntax that works well in practice, and some ineffable thing that makes libraries interoperate well.

We also added some things that were not in C or C++, like slices and maps, composite literals, expressions at the top level of the file (which is a huge thing that mostly goes unremarked), reflection, garbage collection, and so on. Concurrency, too, naturally.
我们也添加了一些 C 或者 C++ 没有的东西,例如 slice 和 map,复合声明,每个文件的顶级表达式(一个差点被忘记的重要东西),反射,垃圾收集,等等。d当然,还有并发。

One thing that is conspicuously absent is of course a type hierarchy. Allow me to be rude about that for a minute.

Early in the rollout of Go I was told by someone that he could not imagine working in a language without generic types. As I have reported elsewhere, I found that an odd remark.
在 Go 最初的版本中,有人告诉我他无法想像用一个没有范型的语言来工作。就像之前在某些地方提到过的,我认为这绝对是神奇的评论。

To be fair he was probably saying in his own way that he really liked what the STL does for him in C++. For the purpose of argument, though, let’s take his claim at face value.
公平的说,他可能正在用其自己的方式来表达非常喜欢 STL 在 C++ 中为他做的事情。在辩论的前提下,让我们先相信他的观点。

What it says is that he finds writing containers like lists of ints and maps of strings an unbearable burden. I find that an odd claim. I spend very little of my programming time struggling with those issues, even in languages without generic types.
他说编写像 int 列表或 map string 这样的容器是一个无法忍受的负担。我觉得这是个神奇的观点。即便是那些没有范型的语言,我也只会花费很少的事件在这些问题上。

But more important, what it says is that types are the way to lift that burden. Types. Not polymorphic functions or language primitives or helpers of other kinds, but types.

That’s the detail that sticks with me.

Programmers who come to Go from C++ and Java miss the idea of programming with types, particularly inheritance and subclassing and all that. Perhaps I’m a philistine about types but I’ve never found that model particularly expressive.
从 C++ 和 Java 转过来 Go 的程序员怀念工作在类型上的编程方式,尤其是继承和子类,以及所有相关的内容。可能对于类型来说,我是门外汉,不过我真得从未发现这个模型十分具有表达力。

My late friend Alain Fournier once told me that he considered the lowest form of academic work to be taxonomy. And you know what? Type hierarchies are just taxonomy. You need to decide what piece goes in what box, every type’s parent, whether A inherits from B or B from A. Is a sortable array an array that sorts or a sorter represented by an array? If you believe that types address all design issues you must make that decision.
我已故的朋友 Alain Fournier 有一次告诉我说他认为学术的最低级形式就是分类。那么你知道吗?类型层次化就是分类。你必须对哪块进哪个盒子作出决策,包括每个类型的父级,不论是 A 继承自 B,还是 B 继承自 A。一个可排序的数组是一个排序过的数组还是一个数组表达的排序器?如果你坚信所有问题都是由类型驱动设计的,那么你就必须作出决策。

I believe that’s a preposterous way to think about programming. What matters isn’t the ancestor relations between things but what they can do for you.

That, of course, is where interfaces come into Go. But they’re part of a bigger picture, the true Go philosophy.
当然,这就是接口进入 Go 的地方。但是它们已经是蓝图的一部分,那是真正的 Go 哲学。

If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition.
如果说 C++ 和 Java 是关于类型继承和类型分类的,Go 就是关于组合的。

Doug McIlroy, the eventual inventor of Unix pipes, wrote in 1964 (!):
Unix pipe 的最终发明人 Doug McIlroy 在 1964 (!) 这样写到:

We should have some ways of coupling programs like garden hose–screw in another segment when it becomes necessary to massage data in another way. This is the way of IO also.
我们应当像连接花园里的龙头和软管一样,用某种方式一段一段的将消息数据连接起来。这同样是 IO 使用的办法。
That is the way of Go also. Go takes that idea and pushes it very far. It is a language of composition and coupling.
这也是 Go 使用的办法。Go 用了这个主意,并且将其向前推进了一大步。这是一个关于组合与连接的语言。

The obvious example is the way interfaces give us the composition of components. It doesn’t matter what that thing is, if it implements method M I can just drop it in here.
一个显而易见的例子就是接口为我们提供的组合元件的方式。只要它实现了方法 M,就可以放在合适的地方,而不关心它到底是什么东西。

Another important example is how concurrency gives us the composition of independently executing computations.

And there’s even an unusual (and very simple) form of type composition: embedding.

These compositional techniques are what give Go its flavor, which is profoundly different from the flavor of C++ or Java programs.
这就是 Go 特有的组合技术,滋味与 C++ 或 Java 程序完全不同。


There’s an unrelated aspect of Go’s design I’d like to touch upon: Go was designed to help write big programs, written and maintained by big teams.
有一个与此无关的 Go 设计我想要提一下:Go 被设计用于帮助编写大程序,由大团队编写和维护。

There’s this idea about “programming in the large” and somehow C++ and Java own that domain. I believe that’s just a historical accident, or perhaps an industrial accident. But the widely held belief is that it has something to do with object-oriented design.
有一个观点叫做“大编程”,不知怎么回事 C++ 和 Java 主宰了这个领域。我相信这只是一个历史的失误,或者是一个工业化的事故。但是一个广泛被接受的信念是面向对象的设计可以做些事情。

I don’t buy that at all. Big software needs methodology to be sure, but not nearly as much as it needs strong dependency management and clean interface abstraction and superb documentation tools, none of which is served well by C++ (although Java does noticeably better).
我完全不相信那个观点。大软件确实需要方法论保驾护航,但它不比强大的依赖管理、清晰的接口抽象和优秀的文档工具来得更重要,而这些没有一样是 C++ 做好的事情(尽管 Java 明显做得更好一些)

We don’t know yet, because not enough software has been written in Go, but I’m confident Go will turn out to be a superb language for programming in the large. Time will tell.
我们还不知道,因为没有足够的软件采用 Go 来编写,不过我有自信 Go 将在大编程领域脱颖而出。时间证明一切。


Now, to come back to the surprising question that opened my talk:

Why does Go, a language designed from the ground up for what what C++ is used for, not attract more C++ programmers?
为什么 Go,一个被设计为用于摧毁 C++ 的语言,并未获得 C++ 程序员的芳心?

Jokes aside, I think it’s because Go and C++ are profoundly different philosophically.
撇开玩笑不说,我认为那是因为 Go 和 C++ 有着完全不同的哲学。

C++ is about having it all there at your fingertips. I found this quote on a C++11 FAQ:
C++ 是让你的指尖解决所有的问题。我在 C++11 的 FAQ 上引用了这段内容:

The range of abstractions that C++ can express elegantly, flexibly, and at zero costs compared to hand-crafted specialized code has greatly increased.
C++ 与那些巨大增长的特别编写的手工代码相比,具有更加广泛的抽象,优雅、灵活并且零成本的表达能力。
That way of thinking just isn’t the way Go operates. Zero cost isn’t a goal, at least not zero CPU cost. Go’s claim is that minimizing programmer effort is a more important consideration.
这个思考的方向与 Go 的不同。零成本不是目标,至少不是零 CPU 成本。Go 的主张更多考虑的是最小化程序员的工作量。

Go isn’t all-encompassing. You don’t get everything built in. You don’t have precise control of every nuance of execution. For instance, you don’t have RAII. Instead you get a garbage collector. You don’t even get a memory-freeing function.
Go 不是无所不包的。你无法通过内建获得所有东西。你无法精确控制每个细微的执行。例如没有 RAII。而可以用垃圾收集作为代替。也没有内存释放函数。

What you’re given is a set of powerful but easy to understand, easy to use building blocks from which you can assemble—compose—a solution to your problem. It might not end up quite as fast or as sophisticated or as ideologically motivated as the solution you’d write in some of those other languages, but it’ll almost certainly be easier to write, easier to read, easier to understand, easier to maintain, and maybe safer.

To put it another way, oversimplifying of course:

Python and Ruby programmers come to Go because they don’t have to surrender much expressiveness, but gain performance and get to play with concurrency.
Python 和 Ruby 程序员转到 Go 是因为他们并未放弃太多的表达能力,但是获得了性能,并且与并发共舞。

C++ programmers don’t come to Go because they have fought hard to gain exquisite control of their programming domain, and don’t want to surrender any of it. To them, software isn’t just about getting the job done, it’s about doing it a certain way.
C++ 程序员无法转到 Go 是因为他们经过艰辛的战斗才获得对其语言的精确控制能力,他们不想放弃已经获得的任何东西。对于他们,软件不仅仅是关于让工作完成,而是关于用一个确定的方式完成。

The issue, then, is that Go’s success would contradict their world view.
那么,问题是,Go 的成功能否反驳他们的世界观。

And we should have realized that from the beginning. People who are excited about C++11′s new features are not going to care about a language that has so much less. Even if, in the end, it offers so much more.
我们应当在一开始的时候就意识这一点。那些为 C++11 的新特性而兴奋的人们是不会在意一个没有这么多特性的语言的。即便最后发现这个语言能够比他们所想象的提供更多。

Thank you.



Some notes about building Caffe RC3 with Mac OS X 10.11.3, Anaconda, CUDA 7.5, cuDNN 4, Intel MKL and MATLAB R2015b

Mac OS X 10.10安装深度学习框架Caffe教程