Less is exponentially more

 (原文出处:rob pike 博客,https://commandcenter.blogspot.jp/2012/06/less-is-exponentially-more.html )

Here is the text of the talk I gave at the Go SF meeting in June, 2012.

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.

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++.

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.

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.

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.

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).

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.

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.

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.

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.

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.

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.

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.

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.

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.

Robert Empty interfaces: interface {}. These are implemented by all interfaces, and thus this could take the place of 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.

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.

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++:

  • 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
  • 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)
  • 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 postdecrement are statements, not expressions
  • no preincrement or predecrement
  • assignment is not an expression
  • evaluation order defined in assignment, function call (no "sequence point")
  • no pointer arithmetic
  • memory is always zeroed
  • legal to take address of local variable
  • no "this" in methods
  • segmented stacks
  • no const or other type annotations
  • no templates
  • no exceptions
  • builtin 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.
  • 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.

    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.

    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.

    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.

    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.

    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.

    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.

    If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition.

    Doug McIlroy, the eventual inventor of Unix pipes, wrote in 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.


    That is the way of Go also. Go takes that idea and pushes it very far. It is a language of composition and coupling.

    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.

    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.

    ===========

    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.

    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.

    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).

    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.

    ===========

    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?

    Jokes aside, I think it's because Go and C++ are profoundly different philosophically.

    C++ is about having it all there at your fingertips. I found this quote on a 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.


    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 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.

    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.

    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.

    The issue, then, is that Go's success would contradict their world view.

    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.

    Thank you.

Less is exponentially more的更多相关文章

  1. 说说Golang的使用心得

    13年上半年接触了Golang,对Golang十分喜爱.现在是2015年,离春节还有几天,从开始学习到现在的一年半时间里,前前后后也用Golang写了些代码,其中包括业余时间的,也有产品项目中的.一直 ...

  2. angular2系列教程(六)两种pipe:函数式编程与面向对象编程

    今天,我们要讲的是angualr2的pipe这个知识点. 例子

  3. 比特币_Bitcoin 简介

    2008-11   Satoshi Nakamoto  Bitcoin: A Peer-to-Peer Electronic Cash System http://p2pbucks.com/?p=99 ...

  4. Android Lint Checks

    Android Lint Checks Here are the current list of checks that lint performs as of Android Studio 2.3 ...

  5. 自然语言26_perplexity信息

    http://www.ithao123.cn/content-296918.html 首页 > 技术 > 编程 > Python > Python 文本挖掘:简单的自然语言统计 ...

  6. tcp连接listen的backlog剖析

    TCP连接中,最重要的是连接TCP连接上,两个方向之间的各个状态及各个系统调用与状态之间的关系.往往可以以两种图表示,第一种是状态转换图,第二种是连接时序图.如下: 状态图: 时序图:         ...

  7. HTML5资料

    1 Canvas教程 <canvas>是一个新的用于通过脚本(通常是JavaScript)绘图的HTML元素.例如,他可以用于绘图.制作图片的组合或者简单的动画(当然并不那么简单).It ...

  8. Fuzzy Probability Theory---(2)Computing Fuzzy Probabilities

    Let $X=\{x_1,x_2,...,x_n\}$ be a finite set and let $P$ be a probability function defined on all sub ...

  9. [algorithm] My rookie plan to start

    若干年后,经验有一些,但根基不牢靠.[algorithm] series 借助学习Standard Template Library: Algorithms的这段时期,在自己的算法和c++基础方面加些 ...

随机推荐

  1. linux磁盘管理系列-软RAID的实现

    1 什么是RAID RAID全称是独立磁盘冗余阵列(Redundant Array of Independent Disks),基本思想是把多个磁盘组合起来,组合一个磁盘阵列组,使得性能大幅提高. R ...

  2. Undefined index: HTTP_RAW_POST_DATA的解决办法

    $postStr = $GLOBALS["HTTP_RAW_POST_DATA"]; 替换为 $postStr = isset($GLOBALS['HTTP_RAW_POST_DA ...

  3. FreeRTOS--疑难解答

    此章节涉及新手最常遇见的3种问题: 错误的中断优先级设置 栈溢出 不恰当的使用printf() 使用configASSERT()能够显著地提高生产效率,它能够捕获.识别多种类型的错误.强烈建议在开发或 ...

  4. The authenticity of host 'github.com (192.30.253.113)' can't be established.

    在初始化git之后(git init),同时在github建立好仓库之后,本地也新增了ssh kye(ssh-keygen -t rsa -C 'mail address'),同时也在本地新增了远程仓 ...

  5. Coursera课程 Programming Languages, Part A 总结

    Coursera CSE341: Programming Languages 感谢华盛顿大学 Dan Grossman 老师 以及 Coursera . 碎言碎语 这只是 Programming La ...

  6. java基础进阶一:String源码和String常量池

    作者:NiceCui 本文谢绝转载,如需转载需征得作者本人同意,谢谢. 本文链接:http://www.cnblogs.com/NiceCui/p/8046564.html 邮箱:moyi@moyib ...

  7. IP协议和网络传输中的封装与分用。

    关于七层模型和四层模型可以参考这个:http://www.cnblogs.com/xcywt/p/5027277.html 因为四层模型用的比较多,这里只拿四层模型来分析. 1.四层模型中的最下层是链 ...

  8. tiny210(s5pv210)移植u-boot(基于 2014.4 版本号)——NAND 8位硬件ECC

    这节我们实现nand的ecc,保存环境变量到nand flash 中.然后把我们之前的led灯烧写到nand flash 中.开机启动.在 tiny210.h 中定义宏 CONFIG_S5PV210_ ...

  9. This version of the rendering library is more recent than your version of IntelliJ IDEA.

    今天往idea里导入其它项目时又遇到了一个问题.单独标记一下. 导入后打开一个布局xml文件,发现不能渲染,报错为: This version of the rendering library is ...

  10. Git(二)Git几个区的关系与Git和GitHub的关联

    前言 前面只是大概的介绍了一点基础的东西,接下来会更加深入的去了解一下Git. 一.Git的工作区.暂存区和版本库之间的区别和联系 1)工作区 在PC中能看得到的创建的一个管理仓库的目录.比如目录下G ...