标签 BCPL 下的文章

Go语言的遗产

本文是gohugo作者Steve Francia在意大利佛罗伦萨举办的GoLab上分享的闭幕演讲讲稿的文字版,该演讲的主题为”Go的遗产”。该演讲讨论了Go语言继承的遗产,以及它是如何尊重这些遗产的,并在最后总结了Go希望留给后来者的遗产。

img{512x368}

演讲胶片

我们有责任保留好留给我们的遗产,并留下值得我们子孙后代继承的遗产 – 克里斯汀·格雷格(Christine Gregoire)

1. Go语言之前

img{512x368}

1950年

在1950年代后期,人们对每台新计算机如何产生自己独特的语言而感到不安。当时,编程语言是由硬件制造商提供的,并且因型号而异。跨计算机且保持一致的第一门编程语言是Fortran,但这仍然仅适用于其制造商IBM(生产的计算机)。而后,人们成立了一个委员会,该委员会的使命就是设计第一种真正通用的、独立于机器的编程语言。

img{512x368}
图:编程语言历史Babel塔,CACM封面,1961年1月

1960年

1960年1月,有13位计算机科学家在巴黎举行了一次空前的会议,旨在(设计)开发出这样一种语言。美国派出了6位代表,欧洲派出了7位代表。

会议上无休止、令人振奋的讨论也让科学家们筋疲力尽。当一个人的好主意与他人的坏主意一起被抛弃时,一个人就会变得更加恼火。然而,在整个会议期间内,大家都没有懈怠,持续地努力投入着。

最终这13名科学家的思想碰撞产生了良好的化学反应 – Alan Perlis

Algol

img{512x368}

这是一种远远超越其时代的语言,它不仅是对其前辈的一种改进,而且对其所有后继者也产生了重大影响。- Tony Hoare关于编程语言设计的提示– 1973年

img{512x368}

之后,原本一脉相承的语言出现了分裂:

  • Pascal这一分支在欧洲蓬勃发展,有许多继任者,包括ModulaOberon

img{512x368}

  • C语言在美国激增,激发和促进了C++、C#、Java以及JavaScript、Python、Perl、PHP和许多其他语言的诞生和发展。

img{512x368}

  • 到2007年,存在的数十种编程语言都可以追溯到其共同祖先:Algol。

img{512x368}

1964年

我们的并发故事始于Doug McIlroy,他在1964年提出了一些新的想法,这些想法最终演化为Unix Pipes。

当有必要以另一种方式处理数据时,我们应该有一些耦合程序的方法,例如像将花园软管拧入另一部分那样。这也是IO的方式。- Doug McIlroy

背后故事

在1970年至1972年的一段时间内,我不时说:“如何做这样的事情?”,然后我提出了另一个建议,另一个建议,另一个建议。有一天,我想出了一种shell语法用于支持管道使用,Ken说:“我要去实现它!”他厌倦了听到所有这些内容……[并且]他说,“要去实现它”。他没有完全按照我为管道系统调用所建议的去做。他发明了一种更好一点的东西,终于又改变了今天的样子。他在一夜之间将管道符放入了Unix(并且他做到了)……。

麦克罗伊(McIlroy)引述:布赖恩(Brian)的墙上还挂着一张纸,在那张纸上我谈到了像花园软管那样将流(stream)拧在一起。所以这个想法在我脑海中徘徊了很长时间。

同时,在Thompson和Ritchie在黑板上,草拟了一个文件系统,我正在草拟如何在黑板上进行数据处理,方法是将一系列过程串联在一起,并寻找一种将过程连接在一起的前缀表示法语言。之所以失败,是因为很容易说出“cat into grep into……”或“who into cat into grep”等等。这么说很容易,而且从一开始就很清楚,这就是您想说的。但是这些命令具有所有这些附带的参数。它们不仅具有输入和输出参数,还具有选项,并且在语法上还不清楚如何将这些选项插入以前缀表示法编写的链中,比如:cat(grep(who …))。在句法上很多人不知道如何做。所以我把这些非常漂亮的程序写在黑板上,用的语言不够强大,无法应付现实。因此,我们实际上并未这样做。

1978年

到1978年,在对多处理器进行编程的背景下,有许多提议的方法被用于通信和同步。共享内存是最常见的通信机制。

托尼·霍尔(Tony Hoare)发表了一篇论文,该论文改变了一切。它比时代提前了几十年。他称他的论文为:”通讯顺序进程,communicating sequential processes”,就是大家熟知的CSP。

img{512x368}

  • 进程(Processes):执行单元
  • 顺序(Sequential):每个进程都作为一个普通的单线程程序运行
  • 通讯(Communicating):进程如何协调
  • 没有内存共享
  • 没有线程,没有互斥体

Hoare的论文提出了一种语言,每个进程(或者作为一个普通的单线程程序)按顺序执行,通过无缓冲通道(unbuffered channel)相互通信。Hoare的通信进程比典型的Unix Shell管道更通用,因为它们可以以任意模式连接。

img{512x368}

三个语言分支因Hoare的CSP论文而诞生:ErlangOccamNewsqueak

  • 1983年诞生的Occam最接近CSP论文(由Hoare推荐)
  • Erlang在80年代后期专注于CSP的功能方面,并使用mailbox在进程之间进行通信
  • Rob Pike(Newsqueak之父)追逐了并发白鲸(the concurrency white whale)长达20年

img{512x368}

Go是第一种可以同时拥有欧洲和美国语言设计分支传统的语言。实际上,它已经统一了这三个分支

2016年,黑客新闻评论(Hacker News)上的一则帖子称Go语言时停留在70年代的一种语言,这引起了一些对Go的批评……

2. 对过去伟大思想的复兴

img{512x368}

编程语言发展的四波浪潮

img{512x368}

  • 第一波浪潮:语言扩张 – 巴别塔

特征:多样化。很久以前,语言是多种多样的,并在在思想、方法和意见等方面体现出多样性。

  • 第二波浪潮:语言的标准化

特征:快速、复杂且对开发不友好。语言的标准化发生了数十年。到2000年代,事情开始停滞。他们融合为两个阵营:Java/JVM和C/CLR。C++、Java、C#都非常相似。

  • 第三波浪潮:脚本语言

特征:慢、不安全但对开发友好。脚本语言作为对上述语言的复杂性和痛苦的回应而应运而生。它们开发快速而松散,对开发人员友好,但缺乏性能和安全性。

  • 第四波浪潮:恢复

特征:快速、安全、对开发人员友好

Go是对这些语言的复杂性和痛苦的一种反应,也是对脚本语言快速开发和松散本质的反应。

Go恢复了早期语言的简单性和灵活性,增加了现代语言的安全性和开发友好性。Go以一种非常真实的方式复兴了许多伟大的想法,这些想法终于准备就绪。

Go给人的感觉就像是来自60年代,70年代,80年代,90年代,00s,10年代的语言……Steve Francia 2019

Go感觉像这样是因为它由过去60年来的许多伟大构想组成。

img{512x368}

现在,我想谈谈Go中的3种特定功能(简单、并发和Go的OO)以及这些思想起源的4种语言(Oberon、Newsqueak、Simula和Smalltalk)。在Go恢复它们之前,许多思想被遗忘了。

1988年

简单易读的结构和语法: Oberon&C

Niklaus Wirth负责Algol-W,Pascal,Modula。现在是1988年,他的最新语言是Oberon

Oberon的程序结构,以“hello, world”例子为例:

MODULE hello;

IMPORT Out;

BEGIN
    Out.String("Hello, World"); Out.Ln
END hello.

Oberon围绕着爱因斯坦(Albert Einstein)的座右铭设计:“使事情尽可能简单,但不要过于简单。”

程序结构非常简单。

下面是Go的”hello world”程序结构:

package main

import "fmt"

func main(){
    fmt.Println"hello world")
}

这个例子看起来应该很熟悉,它直接采用Oberon的结构。

我们再来看看Oberon的声明结构

CONST n = 42;
TYPE mystring = ARRAY 32 OF CHAR;
VAR s: mystring;

PROCEDURE squared(x:INTEGER):INTEGER;
BEGIN
    RETURN x * x
END squared;
VAR b,c: INTEGER = 1,2;

再来看看Go的声明结构:

const n = 42

type mystring string

var s mystring

func squared(x int) int {
    return x*x
}

var b, c int = 1, 2

在Go和Oberon中,声明都是从左到右(名称,类型,可选值),这恰与C相反,在C语言中,类型放在前面。

很多人看到Go后会问为什么我们要翻转C语法,他们错误地认为Go的声明结构来自C语言。它不是,它来自Oberon。

Go使用了Oberon形态,但却用C的token:

  • {}代替BEGIN END
  • ++,– 代替(内置)的INC和DEC
  • != 代替#
  • %代替MOD
  • || 代替OR
  • []代替ARRAY
  • 结构体代替RECORD
  • *代替POINTER TO

虽然结构来自Oberon,但Go使用的token却来自C。

  • 这里没有太多,这就是重点。语法和结构都很简单。
  • 没有继承,没有层次。没有复杂的作用域(scope)系统。
  • 它们尽可能简单,但并不过于简单。

您可以看到Go如何采用Oberon的简单结构,但是删除了笨拙的语法,并采用C语言的更加优雅和熟悉的语法替换了它们。

这样做的结果是一种非常易读的语言诞生了。

1989年

并发与Newsqueak

罗伯·派克(Rob Pike),他于1989年在贝尔实验室工作。他在这里设计了Newsqueak

  • Newsqueak是一门用于研究和探索的编程语言
  • 它致力于在Sequeak基础上添加实用的、切实可行的并发(concurrency)支持
  • Newsqueak语法上类似C
  • 像CSP一样,Newsqueak使用Channel作为Process的集合点

Rob Pike的Newsqueak在语法上看起来像C,但对并发支持的更好。Squeak用于设计菜单和滚动条之类的设备,Newsqueak解决了同样的问题,但涉及范围更广:Newsqueak用于编写整个应用程序,尤其是窗口系统。

Newsqueak-Prime Sieve pt.1

译注:Rob Pike拿手的素数筛例子

counter := prog(end: int, c: chan of int) {
    i: int;
    for(i = 2; i<end; i++) c<-=i;
};

filter := prog(prime: int, listen, send: chan of int) {
    i: int;
    for(;;)
        if((i=<-listen)%prime)
            send<-=i;
};

Newsqueak-Prime Sieve pt.2

sieve := prog(c: chan of int) {
    for(;;) {
        prime := <-c;
        print(prime, “ “);
        newc := mk(chan of int);
        begin filter(prime, c, newc);
        c = newc;
    }
};

count := mk(chan of int);

begin counter(10000, count);
sieve(count);

与CSP和Squeak不同,Newsqueak将channel视为一等公民:channel可以存储在变量中,可以作为参数传递给函数,甚至channel自身也可以通过channel发送。

另外”<-c(receive)”表达式也是第一次在这里介绍。

channel和routine

Go:

c := make(chan int)
c <- 1
x = <-c
go f(x)

vs.

Newsqueak:

c := mk(chan of int);
c <- = 1;
x = <-c;
begin f(x);

我们看到:Go的并发方法几乎与Newsqueak完全相同,channel和 goroutines的使用方式也是相同的。

select

Newsqueak还使用了看起来与Go的select语句非常相似的select。

select {
    case msg1 = <-c1:
        print(“received”, msg1, “\n”);
    case msg2 = <-c2:
        print(“received”, msg2, “\n”);
}

您可以清楚地看到Go并发的基础在25年前是如何在Newqueak中被建立起来的。Go采纳了这些”老想法”,并对其进行了改进,使其可以投入生产。

Ryan Dahl: Node.js的创建者的访谈(2017)

我喜欢Go的编程模型。使用goroutine是如此简单和有趣……如果您要构建服务器,那么我无法想象使用Go以外的任何工具。Goroutine使Go的并发变得简单。

1965年

面向对象基础(Smalltalk)

OO在C++/Java之前就已存在,在C++和Java重新定义面向对象之前。

什么是面向对象?

  • 附加到数据对象的过程(Procedure)
  • Procedure的可重用性

Procedure+数据

img{512x368}

Simula继承了Algol,并在其中添加了对象,类,继承和子类。 Simula被认为是第一种面向对象的编程语言,并且在Smalltalk和所有随后的OO语言的开发中具有重要的影响力。

Simula改变了一直以来的从Procedure的角度来看的思维方式,…他将其翻转为…面向对象的视角,即在每种类型的对象中,您都有处理它的所有方法。- Small Talk的实现者Dan Ingalls

1980

接下来是Smalltalk,其中一切都是对象,并且仅通过发送消息与对象进行通信。

img{512x368}

我确实发明了“面向对象”这个术语,但这是一个错误的选择,因为它没有强调消息发送这个更重要的思想。 – Alan Kay: 从A到Z的编程语言:Smalltalk-80 – 2010

1989年

Procedure重用

我们将讨论两个出版物:

如果系统的任何部分取决于另一部分的内部结构,那么复杂度会随着系统大小的平方而增加 – Dan Ingalls面向对象编程— 1989年

继承。 我们看到了继承带来的这种指数级复杂性

对于使用半新的OO语言进行编程的任何人,这应该看起来都很熟悉。关系线无处不在。 – SPAGHETTI CODE的诞生

论文《强类型面向对象编程的接口》中所提到的系统提供了Ada和Modula-2之类的语言中的模块接口的优点,同时保留了可表达性,使无类型的面向对象的语言(如Smalltalk)具有灵活性。

Go interfaces

type Point interface {
    X() int
    Y() int
    Move(int,int)
    Point Equal(Point) bool
}

Go团队在实现interface时并不知道到该论文的存在。由于这两种方法的明显相似性,后来与他们share了该论文。

Go采取了非常相似的方法,但是对上面论文中想法进行了改进,因为Go接口是隐式的,这使Go应用程序解耦并提供了极大的灵活性。

当您尝试分解一个复杂的问题时,您想要尝试将其分解为尽可能少的部分,并且希望它们尽可能独立。 – Dan Ingalls 面向对象编程— 1989

Go的interface和method采用尽可能独立的方式。只要添加正确的方法,任何类型都可以满足任何接口。可以在满足该接口的类型之前或之后定义一个接口。事实证明,这种方式是有效的,而且效果很好。

Go的OO

  • method提供任何类型的消息发送机制
  • 接口通过动态调度多态性提供可重用性

Go提供了像Smalltalk定义的那种面向对象编程,只是更加贴近实际,即使它不包含类,对象或继承。

  • Smalltalk: OO是关于消息发送
  • Go的interface允许方法像Smalltalk的消息一样自由使用,但是是在一种有类型的语言中使用

3. Go的设计哲学

img{512x368}

2007年

在一次耗时45分钟的C++构建过程中……

罗勃·派克:把时钟拨回到2007年9月,当时我正在对一个巨大的谷歌C++程序做一些微小但重要的优化工作,你们都与这个庞大的程序做过交互。我得这个编译过程在我们的巨大的分布式编译集群上跑了约45分钟。我收到一条消息:为C++标准委员会服务的几位Google员工将进行一个演讲,他们将告诉我们C++ 11的新功能。

在一个小时的演讲中,我们听到了有关计划中的35个新功能的消息。……这时我问自己一个问题:”C++委员会真的相信C++的不足之处在于它没有足够的功能吗?” 当然……,简化语言而不是为其添加更多功能将是一个更大的成就。Rob Pike和他的办公室同事(Robert Griesemer、Ken Thompson)回到了办公桌前。这真的让他们开始思考…

现代实用的编程语言应该是什么样?到45分钟构建完成时,他们已经有了一个充满想法的白板。

语言设计的进化过程

我们从头开始构建,仅从C中借鉴了一些小东西,例如运算符和大括号,以及一些通用关键字。当然,我们还借鉴了我们所知道的其他语言的想法。- 罗伯·派克(Rob Pike)

少即是(指数级的)多 – 2012年,在谈到Go的灵感时 Rob Pike

Go的众多祖先和对Go有影响的语言:

img{512x368}

我要说的是,没有哪位语言设计师比这三位语言设计师(Rob Pike, Robert Griesemer, Ken Thompson) 具有更广泛或更深的语言设计专业知识。他们对以前发生的事情有很丰富的了解,他们知道该采摘什么。他们还具有事后观察的优势(后发优势)。这是修复他们认为可以做得更好的事情的机会。

进化不是革命

  • 原则1:大多数思想都来自先前的思想

大多数思想根本不是新事物

进化不是革命:新语言应该巩固而不是发明新特性

等待良好的设计

  • 原则2:No是暂时的,Yes是永远的。

在Go的整个历史中,有很多这样的实例。通常的想法是,在设计语言时,不会出现“撤消(undo)”的情况。如果您今天说“No”,那么您明天总是可以说“Yes”,但是如果今天您说“Yes”,那么您将在很长一段时间或永远被它“困”住…。

如有疑问,请将其排除在外。- Joshua Bloch:关于设计的对话– 2002

共识驱动的设计

  • 原则3: 应该使一切都尽可能简单,但不要过于简单。-爱因斯坦

当我们三个人开始时,这纯粹是研究。…我们从一个想法开始,即我们三个人都必须针对该语言的每个特性进行讨论,因此,无论出于何种原因,都不会在该语言中放入多余的垃圾。 – 肯·汤普森(Ken Thompson)访谈– 2011年,肯从Bell Labs学习了这种做法

有两种构建软件设计的方法。一种方法是使其变得如此简单,以至于显然没有缺陷。另一种方法是使其变得如此复杂,以至于没有明显的缺陷。 – 托尼·霍尔(Tony Hoare)皇帝的旧衣服-1981年,Go采取了第一种方法,而大多数其他语言都采用第二种方法。

快速迭代期待并实现大规模改变

  • 最后一个原则是快速迭代的原则。

当您处于语言的设计阶段时,您将需要进行频繁且有时是巨大的更改。朝着这个期望前进,并围绕它建立您的流程。

4. 今天的Go

img{512x368}

我们来看Go如今是如何演变的。

2019年

Go今天是如何继续进行演化的。

上面的4条原则在该语言的初期,在发行稳定版之前和被采用之前都非常有效。

但我们现在的处境非常不同。我们不再能够将所有贡献者都放在白板上,甚至不能放在如此大的房间中(译注:Go目前的contributor数量庞大)。

现在,我想与大家分享Go项目今天如何进行更改的。

我们的原则是“等待良好的设计”,这似乎意味着这是一种消极的活动,但这与事实相去甚远。真正的意思是,除非我们非常有信心采用正确的方法,否则我们不会接受更改。

这意味着所有问题的默认答案是“否”。“是”的成本非常高,因此需要一个压倒性的理由。

对一件事说“Yes”意味着对其他一切都说“No”。

软件复杂性的主要原因是供应商不加批判地采用了用户想要的几乎所有功能。人们似乎将复杂误解为先进。

不可理解的应该引起怀疑而不是钦佩。- Niklaus Wirth, 1995年

我们对Go进行了长期展望。为下一个十年或两个或更多个而设计。大多数项目的运行时间要短得多,因此通常会接受第一个可通过的解决方案。

随着时间的流逝,经过长时间这种训练的人们已经意识到:如果一个好主意会被接受,或者反之,不好的主意会被拒绝。

由于我们的长期观点,在为Go项目做出贡献时人们挣扎并不罕见。当他们的想法不能被接受时,许多人感到被亲自拒绝。

或更糟糕的是,人们会感到自己不合格或不称职。我记得有这种感觉。

几年前,我创建了一个网站引擎Hugo,随着时间的推移,它成为Go模板的第一用户,并在此过程中发现了几个问题。尽管如此,我感到非常没有资格报告这些问题,因为我认为创建这些库的“专家”显然比我了解更多,并且我无能为力。在第一次或第二次Gophercon上,我碰巧在午餐台上站在Russ Cox旁边,我们开始交谈。他强烈鼓励我报告这些问题,并让我知道他们多么地需要反馈。

几年后,我加入了Go团队,并从这个经验中学到了很多。我观察到的一件事是,Go团队那些加入较久的核心成员有一件事比大多数其他成员都做得更好,这可能不是您的想法。Go团队的老成员已经非常习惯于听到“不”的声音。我们团队成员的提议被拒绝的比率很高,甚至高于Go团队之外的提议。我们已经了解到,每个“No”都与拥有正确的“Yes”仅一步之遥。

因为我们经常听到“No”的声音,所以我们同情别人被拒绝的感觉。

今天我要传达给您的信息是您受到重视和需要。请继续尝试。在接下来的十年或二十年或更长的时间内,您是Go演化的关键部分。

Go开发流程

Go开发流程

实验流程简化始于今年早些时候,Russ Cox谈论了我们用来对Go进行更改的流程以及它的演变方式。在演讲中,他讨论了实验的两个步骤,并简化了我们的迭代过程。

我们的过程不是为了速度而建立的,而是为了正确。我们花费大量时间进行实验和简化,然后完善自己的想法,直到它们正确为止。

你们都是Go伟大实验的一部分,并且是继续构建Go的过程的关键部分

我想与大家分享3种方法,每个人都可以为Go做出贡献。

  • 使用Go -> 识别问题 -> 您遇到的事情/体验并写下来。
  • 您有想法-> 编写建议 -> 纳入反馈
  • 您阅读提案 -> 阅读评论 > 添加您的声音

img{512x368}

Go开发过程:实验 -> 简化 -> 最终交付。通过此提炼过程,想法将准备就绪,我们将进行交付。我想对过程的这一部分及其工作原理提供更多见解。

共识驱动的设计

  • 误解:谷歌有一小群“决策者”
  • 真相:评论者之间达成共识

关于提案过程的事实

  • 事实上,大多提案提案都很小
  • 几乎所有提案的讨论最终都在参与者(评论员)之间达成了共识。
  • 提案审核委员会主要进行一些“园艺劳动”(译者:社区行为培养)

您看到这不是一件非常迷人的工作。我们评论的大多数问题都要求您澄清问题或什么也不做,让对话继续进行。我们还会考虑谁在对话中丢失,并邀请他们加入对话。

当讨论似乎已经解决(赞成或反对)时,我们将关闭其中的一小部分。

让我们看一下最近的一个建议。这只是从最近提案池中随机选取的一个。

它具有一些有趣的属性:大量参与,来自9个参与者的25条评论引用用户问题(体验)。早期该issue尚无共识(由点赞决定)。

在对该想法进行讨论和完善之后,很明显已经达成了普遍共识。

它被标记为“可能接受”,并且留下足够的时间窗口允许任何人提供我们不接受的理由。

这是一组最近审核的提案。您会注意到,他们每个人都引用了之前的评论,并根据这些评论提出了建议。

在提案审核委员会中,通常会有一个人留下评论,但代表所有出席者。Russ Cox通常志愿承担了这个角色,这就是为什么所有这些issue上面都加上他的名字的原因。在大多数情况下,此窗口不会附加注释。我们觉得这个窗口虽然很少使用,但对于建立共识的过程至关重要。

变化是缓慢发生的

这是设计使然。这是缓慢的、谨慎和有条不紊的,以确保我们最终达到想要的目标。

过去十年的主要里程碑

尽管Go的变化缓慢,但增长迅速。我想了解一下过去十年中的一些主要里程碑。

img{512x368}

  • 2009年, Go语言开源,Gopher诞生,Go脱离了Google的实验场;
  • 2010年,获得年度TIOBE语言,Bossie奖,引入append和go tour;
  • 2011年,gccgo合并到GCC中,引入gofix,YouTube在生产中采用了Go;
  • 2012年,Go 1.0发布!发布Go1兼容性承诺;在Google内部发布第一项Go生产服务

img{512x368}

  • 2013年,Packer,Docker,Hugo用Go编写;6个月发布周期;第一个Go大会举行(日本东京)
  • 2014年,Kubernetes使用Go开发;代码仓库由Mercurial→Git;第一次美国和欧洲会议;Go项目贡献者达到500名;
  • 2015年,Go编译器使用Go重写,实现自举;GC精化; Women Who Go&GoBridge born; 印度、中国第一次go大会举行;
  • 2016年,支持HTTP/2和Context;第一次拉丁&中东Go大会举行;最受喜欢的5门编程语言;第一次Go用户调查;贡献者达1000名;

img{512x368}

  • 2017年,GC小于ms级的暂停; 引入type alias;开发人员想要使用编程语言第一名第一次); 13次会议; 第一届贡献者峰会
  • 2018年,引入Go模块;来自Go团队之外的贡献者人数首次超过Go团队;19次Go会议;Go新品牌和logo发布;PR数在github排名第四; 开发人员打算学习的语言中排名第一

5. Go的遗产

img{512x368}

没有时间机器可以达到未来。未来的到来缓慢而又出乎意料。我们不知道Go或世界将会发生什么。但是我们确实知道我们想留下什么标记。

  • 我们希望Go能够留下创新的遗产

Go向主流受众带来了创新的想法,例如goroutine,channel,简单的interface。这些想法现在正在其他语言中出现,我们为这一趋势继续感到高兴。

Go fmt于2009年推出时颇有争议。现在,大多数语言都采用了类似的方法。

也许我们最有影响力的遗产将是,我们像Go一样激励人们挑战既定的规范,并在各处寻找灵感。

  • 我们希望Go留下增强信心和能力的遗产

Go使开发人员能够编写生产服务器软件而无需C和C++所需的额外专业知识,而无需现代Java的复杂性,也无需解释语言的性能成本。

Go比其他任何语言都更能使人们把他们的想法变成现实。当我第一次开始撰写Hugo时,我个人感觉到了这一点,这是Go最吸引我的地方。

其他那些也被赋予了类似的能力和信心的人,其中许多人在本次会议上谈到了Go的创造性用途,包括Florin的家庭自动化研讨会,Ron的机器人,Elias的GUI等。

  • Go改变生活。

我有幸环游世界,在任何地方遇到的男人和女人,他们通常没有CS背景或学位,但能够学习Go并用它来创办公司,获得更好的工作并改善他们和他们家人的生活。

遗产不会为人们留下任何东西。它在人们身上留下了一些东西。- 彼得·斯特普尔

我们每个人都受到过往历史的影响。我们是遗产。我们被影响,我们影响别人。


我的网课“Kubernetes实战:高可用集群搭建、配置、运维与应用”在慕课网上线了,感谢小伙伴们学习支持!

我爱发短信:企业级短信平台定制开发专家 https://tonybai.com/
smspush : 可部署在企业内部的定制化短信平台,三网覆盖,不惧大并发接入,可定制扩展; 短信内容你来定,不再受约束, 接口丰富,支持长短信,签名可选。

著名云主机服务厂商DigitalOcean发布最新的主机计划,入门级Droplet配置升级为:1 core CPU、1G内存、25G高速SSD,价格5$/月。有使用DigitalOcean需求的朋友,可以打开这个链接地址:https://m.do.co/c/bff6eed92687 开启你的DO主机之路。

Gopher Daily(Gopher每日新闻)归档仓库 – https://github.com/bigwhite/gopherdaily

我的联系方式:

微博:https://weibo.com/bigwhite20xx
微信公众号:iamtonybai
博客:tonybai.com
github: https://github.com/bigwhite

微信赞赏:
img{512x368}

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。

Golang的演化历程

本文来自Google的Golang语言设计者之一Rob Pike大神在GopherCon2014大会上的开幕主题演讲资料“Hello, Gophers!”。Rob大神在这次分 享中用了两个生动的例子讲述了Golang的演化历程,总结了Golang到目前为止的成功因素,值得广大Golang Programmer & Beginner学习和了解。这里也用了"Golang的演化历程"作为标题。

1、Hello Gophers!

package main

import "fmt"

func main() {
    fmt.Printf("Hello, gophers!\n")
}

Rob大神的见面礼,后续会有针对这段的演化历史的陈述。

2、历史

这是一个历史性的时刻。

Golang已经获得了一定的成功,值得拥有属于自己的技术大会。

3、成功

促成这份成功的因素有许多:

    – 功能
    – 缺少的功能
    – 功能的组合
    – 设计   
    – 人
    – 时间

4、案例学习:两段程序

我们来近距离回顾两段程序。

第一个是你见过的第一个Go程序,是属于你的历史时刻。
第二个是我们见过的第一个Go程序,是属于全世界所有Gophers的历史时刻。

先看第一个“hello, world”

5、hello.b

main( ) {
    extrn a, b, c;
    putchar(a); putchar(b); putchar(c); putchar('!*n');
}
a 'hell';
b 'o, w';
c 'orld';

上面这段代码首先出现在1972年Brian W. Kernighan的B语言教程中(也有另外一说是出现在那之前的BCPL语言中)。

6、hello.c

main()
{
    printf("hello, world");
}

上面这段代码出现在1974年Brian W. Kernighan编写的《Programming in C: A Tutorial》中。这份教程当时是作为Unix v5文档的一部分。

7、hello.c

main()
{
    printf("hello, world\n"); //译注:与上面的hello.c相比,多了个换行符\n输出
}

这段代码首次出现在1978年Brian W. Kernighan和Dennis M. Ritchie合著的《The C Programming Language》一书中。

8、hello.c, 标准C草案

#include <stdio.h> //译注:与上面hello.c相比, 多了这个头文件包含

main()
{
    printf("hello, world\n");
}

这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《The C Programming Language》第二版一书中,基于标准C草案。

9、hello.c,标准C89

#include <stdio.h>

main(void) //译注:与上面hello.c相比,多了个void
{
    printf("hello, world\n");
}

这段代码出现在1988年Brian W. Kernighan和Dennis M. Ritchie合著的《The C Programming Language》第二版第二次修订中。

10、一两代之后…

(省略所有中间语言)

关于Golang的讨论开始于2007年年末。

第一版语言规范起草于2008年3月份。

用于实验和原型目的的编译器开发工作已经展开。

最初的编译器输出的是C代码。

语言规范一形成,我们就重写了编译器,输出本地代码(机器码)。

11、hello.go, 2008年6月6日

package main

func main() int {
    print "hello, world\n";
    return 0;
}

针对首次提交代码的测试。

内置的print已经是当时的全部实现。main函数返回一个int类型值。
注意:print后面没有括号。

12、hello.go,2008年6月27日

package main

func main() {
    print "hello, world\n";
}

当main函数返回,程序调用exit(0)。

13、hello.go,2008年8月11日

package main

func main() {
    print("hello, world\n");
}

print调用加上了括号,这时print是一个函数,不再是一个原语。

14、hello.go,2008年10月24日

package main

import "fmt"

func main() {
    fmt.printf("hello, world\n");
}

我们熟知并喜欢的printf来了。

15、hello.go,2009年1月15日

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n");
}

头母大写的函数名用作才是导出的符号。

16、hello.go, 2009年12约11日

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

不再需要分号。

这是在2009年11月10日Golang开发发布后的一次重要改变。

这也是当前版本的hello, world

我们花了些时间到达这里(32年!)

都是历史了!

17、不仅仅有C

我们从"C"开始,但Go与C相比有着巨大的不同。

其他一些语言影响和贯穿于Go的设计当中。

    C: 语句和表达式语法
    Pascal: 声明语法
    Modula 2, Oberon 2:包
    CSP, Occam, Newsqueak, Limbo, Alef:  并发
    BCPL: 分号规则
    Smalltalk: 方法(method)
    Newsqueak: <-, :=
    APL: iota

   
等等。也有一些是全新发明的,例如defer、常量。

还有一些来自其他语言的优点和缺点:
    C++, C#, Java, JavaScript, LISP, Python, Scala, …

18、hello.go,Go 1版

将我们带到了今天。

package main

import "fmt"

func main() {
    fmt.Println("Hello, Gophers (some of whom know 中文)!")
}

我们来深入挖掘一下,把这段代码做一个拆解。

19、Hello, World的16个tokens

package
main
import
"fmt"
func
main
(
)
{
fmt
.
Println
(
"Hello, Gophers (some of whom know 中文)!"
)
}

20、package

早期设计讨论的主要话题:扩展性的关键

package是什么?来自Modula-2等语言的idea

为什么是package?

    – 拥有编译构建所需的全部信息
    – 没有循环依赖(import)
    – 没有子包
    – 包名与包路径分离
    – 包级别可见性,而不是类型级别
    – 在包内部,你拥有整个语言,在包外部,你只拥有包许可的东西。

21、main

一个C语言遗留风范尽显之处
最初是Main,原因记不得了。
主要的包,main函数
很特别,因为它是初始化树(initialization tree)的根(root)。

22、import

一种加载包的机制
通过编译器实现(有别于文本预处理器。译注:C语言的include是通过preprocessor实现的)
努力使其高效且线性
导入的一个包,而不是一个标识符(identifiers)集合(译注:C语言的include是将头文件里的标识符集合引入)
至于export,它曾经是一个关键字。

23、"fmt"

包路径(package path)只是一个字符串,并非标识符的列表。
让语言避免定义它的含义 – 适应性。(Allows the language to avoid defining what it means—adaptability)
从一开始就想要一个URL作为一个选项。(译注:类似import "github.com/go/tools/xxx)
可以应付将来的发展。

24、func

一个关键字,用于引入函数(类型、变量、常量),易于编译器解析。
对于函数字面量(闭包)而言,易于解析非常重要。
顺便说一下,最初这个关键字不是func,而是function。

小插曲:

Mail thread from February 6, 2008
From: Ken Thompson <ken@google.com>
To: gri, r
larry and sergey came by tonight. we 
talked about go for more than an hour. 
they both said they liked it very much.
p.s. one of larrys comments was "why isnt function spelled func?"

From: Rob Pike <r@google.com>

To: ken, gri
fine with me. seems compatible with 'var'.
anyway we can always say, "larry said to call it 'func'"

25、main

程序执行的起点。除非它不是。(译注:main不是起点,rob大神的意思是不是指下列情形,比如go test测试包,在google app engine上的go程序不需要main)
将初始化与正常执行分离,早期计划之中的。
初始化在哪里发生的?(译注:说的是package内的func init() {..}函数吧)
回到包设计。(译注:重温golang的package设计思想)

26、()

看看,没有void
main没有返回值,由运行时来处理main的返回后的事情。
没有函数参数(命令行选项通过os包获取)
没有返回值

返回值以及语法

27、{

用的是大括号,而不是空格(译注:估计是与python的空格缩进对比)
同样也不是方括号。
为什么在括号后放置换行符(newline)?

28、fmt

所有导入的标识符均限定于其导入的包。(All imported identifiers are qualified by their import.)
每个标识符要么是包或函数的本地变量,要么被类型或导入包限定。
对代码可读性的重大影响。

为什么是fmt,而不是format?

29、.

句号token在Go中有多少使用?(很多)
a.B的含义需要使用到类型系统
但这对于人类来说非常清晰,读起来也非常容易。
针对指针的自动转换(没有->)。

30、Println

Println,不是println,头母大写才是导出符号。
知道它是反射驱动的(reflection-driven)
可变参数函数
参数类型是(…); 2010年2月1日变成(…interface{})

31、(

传统函数语法

32、"Hello, Gophers (some of whom know 中文)!"

UTF-8编码的源码输入。字符串字面量也自动是utf8编码格式的。

但什么是字符串(string)呢?

首批写入规范的语法规则,今天很难改变了。(blog.golang.org/strings)

33、)

没有分号
在go发布后不久我们就去除了分号
早期曾胡闹地尝试将它们(译注:指得是括号)去掉
最终接受了BCPL的方案

34、}

第一轮结束。

旁白:还没有讨论到的

    – 类型
    – 常量
    – 方法
    – interface
    – 库
    – 内存管理
    – 并发(接下来将讨论)
   
外加工具,生态系统,社区等。
语言是核心,但也只是我们故事的一部分。

35、成功

要素:
    – 站在巨人的肩膀上(building on history)
    – 经验之作(building on experience) 译注:最初的三个神级语言设计者
    – 设计过程
    – 早期idea提炼到最终的方案中
    – 由一个小团队专门集中精力做
   
最终:承诺
    Go 1.0锁定了语言核心与标准库。

36、另一轮

让我们看第二个程序的类似演化过程。

37、问题:素数筛(Prime sieve)

问题来自于Communicating Sequential Processes, by C. A. R. Hoare, 1978。

“问题:以升序打印所有小于10000的素数。使用一个process数组:SIEVE,其中每个process从其前驱元素输入一个素数并打印它。接下 来这个process从其前驱元素接收到一个升序数字流并将它们传给其后继元素,这个过程会剔除掉所有是最初素数整数倍的数字。

38、解决方案

在1978年的CSP论文中。(注意不是Eratosthenes筛)

这个优美的方案是由David Gries贡献出来的。

39、CSP

在Hoare的CSP论文中:

[SIEVE(i:1..100)::
    p,mp:integer;
    SIEVE(i - 1)?p;
    print!p;
    mp := p; comment mp is a multiple of p;
*[m:integer; SIEVE(i - 1)?m →
    *[m > mp → mp := mp + p];
    [m = mp → skip
    ||m < mp → SIEVE(i + 1)!m
]   ]
||SIEVE(0)::print!2; n:integer; n := 3;
    *[n < 10000 → SIEVE(1)!n; n := n + 2]
||SIEVE(101)::*[n:integer;SIEVE(100)?n → print!n]
||print::*[(i:0..101) n:integer; SIEVE(i)?n → ...]
]

没有channel。能处理的素数的个数是在程序中指定的。

40、Newsqueak

circa 1988。

Rob Pike语言设计,Tom Cargill和Doug McIlroy实现。

使用了channels,这样个数是可编程的。(channel这个idea从何而来?)

counter:=prog(end: int, c: chan of int)
{
    i:int;
    for(i=2; i<end; i++)
        c<-=i;
};

filter:=prog(prime: int, listen: chan of int, send: chan of int)
{
    i:int;
    for(;;)
        if((i=<-listen)%prime)
            send<-=i;
};

sieve:=prog(c: chan of int)
{
    for(;;){
        prime:=<-c;
        print(prime, " ");
        newc:=mk(chan of int);
        begin filter(prime, c, newc);
        c=newc;
    }
};

count:=mk(chan of int);

begin counter(10000, count);
begin sieve(count);
"";

41、sieve.go,2008年3月5日

使用go规范编写的第一个版本,可能是第二个由go编写的重要程序。

>用于发送;<用于接收。Channel是指针。Main是头字母大写的。

package Main

// Send the sequence 2, 3, 4, … to channel 'ch'.
func Generate(ch *chan> int) {
    for i := 2; ; i++ {
        >ch = i;    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan< int, out *chan> int, prime int) {
    for ; ; {
        i := <in;    // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            >out = i;    // Send 'i' to channel 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for ; ; {
        prime := <ch;
        printf("%d\n", prime);
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1;
    }
}

func Main() {
    Sieve();
}

42. sieve.go,2008年7月22日

-<用于发送;-<用于接收。Channel仍然是指针。但现在main不是大写字母开头的了。

package main

// Send the sequence 2, 3, 4, … to channel 'ch'.
func Generate(ch *chan-< int) {
    for i := 2; ; i++ {
        ch -< i    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan<- int, out *chan-< int, prime int) {
    for {
        i := <-in;    // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out -< i    // Send 'i' to channel 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        printf("%d\n",    prime);
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

43、sieve.go,2008年9月17日

通信操作符现在是<-。channel仍然是指针。

package main

// Send the sequence 2, 3, 4, … to channel 'ch'.
func Generate(ch *chan <- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan <- int, out *<-chan int, prime int) {
    for {
        i := <-in;  // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out <- i  // Send 'i' to channel 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        print(prime, "\n");
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

44、sieve.go,2009年1月6日

引入了make内置操作符。没有指针。编码错误!(有个*被留下了,错误的参数类型)

package main

// Send the sequence 2, 3, 4, … to channel 'ch'.
func Generate(ch chan <- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in chan <- int, out *<-chan int, prime int) {
    for {
        i := <-in;  // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out <- i  // Send 'i' to channel 'out'.
        }
    }
}

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := make(chan int);  // Create a new channel.
    go Generate(ch);       // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        print(prime, "\n");
        ch1 := make(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

45、sieve.go,2009年9月25日

第一个正确的现代版本。同样,大写头母不见了,使用了fmt。

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i;    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
    for i := range src {    // Loop over values received from 'src'.
        if i%prime != 0 {
            dst <- i;    // Send 'i' to channel 'dst'.
        }
    }
}

// The prime sieve: Daisy-chain filter processes together.
func sieve() {
    ch := make(chan int);  // Create a new channel.
    go generate(ch);       // Start generate() as a subprocess.
    for {
        prime := <-ch;
        fmt.Print(prime, "\n");
        ch1 := make(chan int);
        go filter(ch, ch1, prime);
        ch = ch1;
    }
}

func main() {
    sieve();
}

46、sieve.go,2009年12月10日

分号不见了。程序已经与现在一致了。

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
    for i := range src {  // Loop over values received from 'src'.
        if i%prime != 0 {
            dst <- i  // Send 'i' to channel 'dst'.
        }
    }
}

// The prime sieve: Daisy-chain filter processes together.
func sieve() {
    ch := make(chan int)  // Create a new channel.
    go generate(ch)       // Start generate() as a subprocess.
    for {
        prime := <-ch
        fmt.Print(prime, "\n")
        ch1 := make(chan int)
        go filter(ch, ch1, prime)
        ch = ch1
    }
}

func main() {
    sieve()
}

这个优美的方案来自于几十年的设计过程。

47、旁边,没有讨论到的

select

真实并发程序的核心连接器(connector)
最初起源于Dijkstra的守卫命令(guarded command)
在Hoare的CSP理论实现真正并发。
经过Newsqueak、Alef、Limbo和其他语言改良后

2008年3月26日出现在Go版本中。

简单,澄清,语法方面的考虑。

48、稳定性

Sieve程序自从2009年末就再未改变过。– 稳定!

开源系统并不总是兼容和稳定的。

但,Go是。(兼容和稳定的)

这是Go成功的一个重要原因。

49、趋势

图数据展示了Go 1.0发布后Go语言的爆发。

50、成功

Go成功的元素:

    显然的:功能和工具。

    * 并发
    * 垃圾回收
    * 高效的实现
    * 给人以动态类型体验的静态类型系统
    * 丰富但规模有限的标准库
    * 工具化
    * gofmt
    * 在大规模系统中的应用

    不那么显然的:过程

    * 始终聚焦最初的目标
    * 在冻结后的集中开发
    * 小核心团队易于取得一致
    * 社区的重要贡献
    * 丰富的生态系统
   
总之,开源社区共享了我们的使命,聚焦于为当今的世界设计一门语言。

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言精进之路1 Go语言精进之路2 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats