acm-header
登录

ACM通信

贡献的文章

围棋编程语言和环境


去云里的地鼠吧

图片来源:Andrij Borys Associates;Renee French (CC by 3.0)

Go是2007年底在谷歌上创建的一种编程语言,并于2009年11月作为开源发布。从那时起,它就像一个公共项目一样运作,有成千上万的个人和几十家公司的捐款。Go已经成为一种用于构建云基础设施的流行语言:Linux容器管理器Docker和容器部署系统Kubernetes都是用Go编写的核心云技术。如今,Go是每个主要云提供商的关键基础设施的基础,也是云本地计算基金会(cloud Native Computing foundation)所托管的大多数项目的实现语言。

回到顶部

关键的见解

ins01.gif

早期的用户因为各种各样的原因被围棋所吸引。用于构建系统的垃圾收集、静态编译语言并不常见。Go对并发性和并行性的原生支持有助于利用当时正在成为主流的多核计算机。自包含的二进制文件和容易的交叉编译简化了部署。而谷歌的名字无疑是一场平局。

但用户为什么会留下来呢?为什么围棋越来越受欢迎,而其他许多语言项目却没有呢?我们认为,语言本身只构成了答案的一小部分。整个故事必须涉及整个Go环境:库、工具、约定和软件工程的总体方法,这些都支持用该语言编程。因此,在语言设计中做出的最重要的决定是那些使Go更适合大规模软件工程并帮助我们吸引志同道合的开发人员的决定。

在本文中,我们将探讨我们认为对围棋的成功最重要的设计决策,探索它们如何不仅应用于语言,而且更广泛地应用于环境。很难孤立出任何一个具体决策的贡献,所以这篇文章不应该被当作科学分析来读,而应该被当作基于过去十年围棋经验和用户反馈的我们最好的理解的展示。

回到顶部

起源

Go是通过在谷歌上构建大规模分布式系统的经验产生的,该系统在由数千名软件工程师共享的大型代码库中工作。我们希望为这种环境设计的语言和工具可以解决公司和整个行业面临的挑战。由于开发工作和部署的生产系统的规模,出现了挑战。

发展规模。在开发方面,谷歌在2007年有大约4000名活跃用户在一个单一的、共享的、多语言(c++、Java、Python)代码库中工作。3.单一的代码库使得它很容易修复,例如,内存分配器的问题减慢了主web服务器的速度。但是在处理一个库时,由于很难找到一个包的所有依赖项,很容易在不知情的情况下破坏一个以前不知道的客户机。

此外,在我们使用的现有语言中,导入一个库可能会导致编译器递归地加载所导入的所有库。在2007年的一次c++编译中,我们观察了编译器# include当处理一组总共4.2 MB的文件时,可以读取超过8 GB的数据,对于一个已经很大的程序来说,这是将近2000倍的扩展倍数。如果编译给定源文件所需读取的头文件数量与源树的数量呈线性增长,那么整个源树的编译成本将呈二次增长。

为了弥补这种放缓,我们开始开发一个新的、大规模并行和可缓存的构建系统,这个系统最终成为开源的Bazel构建系统。23但是并行和缓存只能修复效率低下的系统。我们相信语言本身需要提供更多帮助。

生产规模。在生产端,谷歌运行非常大的系统。例如,在2005年3月,一个1500 cpu的Sawzall日志分析系统集群处理2.8 PB的数据。26在2006年8月,谷歌的388个Big-table服务集群包括24500个单独的平板电脑服务器,一组8069个服务器每秒总共处理120万个请求。4

然而,谷歌和其他行业一样,都在努力编写高效的程序来充分利用多核系统。我们的许多系统只能在一台机器上运行同一个二进制文件的多个副本,因为现有的多线程支持既笨拙又低性能。大的、固定大小的线程堆栈、重量级的堆栈交换机以及用于创建新线程和管理它们之间的交互的笨拙语法都使得使用多核系统变得更加困难。但是很明显,服务器的核心数量只会增加。

在这里,我们也相信语言本身可以提供帮助,通过为并发提供轻量级、易于使用的原语。我们还在这些额外的核心中看到了机会:垃圾收集器可以与专用核心上的主程序并行运行,从而降低其延迟成本。

Go是我们对这个问题的答案,这个问题是为了应对这些挑战而设计的语言会是什么样子。毫无疑问,围棋大受欢迎的部分原因在于整个科技行业现在每天都面临着这些挑战。云提供商使得即使是最小的公司也可以针对非常大的生产部署。虽然大多数公司没有数千名活跃的员工编写代码,但现在几乎所有公司都依赖于数千名程序员开发的大量开源基础设施。

本文的其余部分将研究特定的设计决策如何实现扩展开发和生产的目标。我们从核心语言本身开始,并向外扩展到周围的环境。我们不打算对这门语言做一个全面的介绍。关于这一点,请参阅Go语言规范18或者像这样的书Go编程语言。11

回到顶部

围棋程序由一个或多个可导入的包组成,每个包包含一个或多个文件。中的web服务器图1说明了围棋包系统设计的许多重要细节:

f1.jpg
图1。一个Go web服务器。

程序启动一个本地web服务器(第9行),它通过调用你好函数,该函数响应消息“hello, world”(第14行)。

一个包使用显式导入另一个包进口语句(3-6行),与许多语言一样,但与c++的文本相反# include机制。然而,与大多数语言不同的是,Go安排每次导入只读取单个文件。例如,fmt类的公共API引用类型io的第一个参数fmt。流接口值是类型的吗io。作家.在大多数语言中,编译器处理的是fmt也会装载所有的io要搞清楚fmt的定义,这可能反过来需要加载额外的包来理解所有的io的定义。一个import语句可能会处理几十个或数百个包。

与Modula-2类似,Go通过安排,13的编译fmt包的元数据包含关于其自身依赖项的所有必要信息,例如的定义io。作家.因此,编译进口“fmt”只读取完整描述的单个文件fmt及其依赖项。此外,这种扁平化是一次性完成的,当fmt进行编译,避免每次导入时进行多次加载。这种方法使编译器的工作更少,构建速度更快,有助于大规模开发。此外,不允许包导入周期:sincefmt进口io, io不能导入fmt也不需要进口任何东西fmt,即使是间接的。这也会减少编译器的工作,确保特定的构建可以在单独的、单独编译的包的级别上进行拆分。这还支持增量程序分析,我们甚至在运行测试之前运行它来捕获错误,如下所述。

进口fmt不成名io。作家对客户端可用。如果主包想要使用该类型io。作家,它必须进口“输入输出”为本身。因此,一旦所有的引用都指向fmt-限定的名称已从源文件中删除——例如,如果fmt。流调用删除了进口“fmt”语句从源中删除是安全的,无需进一步分析。此属性使在源代码中自动管理导入成为可能。事实上,Go不允许未使用的导入,以避免将未使用的代码链接到程序中而导致的膨胀。

导入路径是带引号的字符串字面值,这使得它们的解释具有灵活性。中导入的包由斜杠分隔进口,但是源代码使用包语句中声明的短标识符引用包。例如,进口"net/http”声明顶级名称http它提供对其内容的访问。在标准库之外,包由以域名开头的类似url的路径标识,如进口“github.com/google/ uuid”.我们稍后将对此类方案进行更多讨论。

最后一个细节是,请注意名称中的大写字母fmt。流而且io。作家.Go类似于c++和Java的公共、私有和受保护的概念和关键字是命名约定。首字母大写的名称,例如Printf而且作家,被“导出”(公共)。不为外人所知。基于案例的、由编译器强制的导出规则应用于包级的常量、函数和类型标识符;方法名称;还有struct字段名。我们采用这一规则是为了避免编写类似关键字的语法权重出口在公共API中涉及的每个标识符旁边。随着时间的推移,我们已经开始重视在每次使用时查看标识符是在包外部可用还是纯粹在内部的能力。

回到顶部

类型

Go提供了常用的基本类型集:布尔值、大小整数,比如uint8而且int32,未分级int而且使用uint(32位或64位,取决于机器的大小),以及浮点数和复数的大小。它提供了指针、固定大小的数组以及与c语言类似的结构体。它还提供了内置的字符串类型、名为map的哈希表以及名为slices的动态大小的数组。大多数围棋程序依赖于这些容器类型,而没有其他特殊的容器类型。

Go不定义类,但允许将方法绑定到任何类型,包括struct、数组、片、映射,甚至基本类型,如整数。它没有类型层次结构;我们觉得继承倾向于使程序在成长过程中更难适应。相反,Go鼓励类型的组合。9


如今,围棋是每个主要云提供商关键基础设施的基础。


Go通过它的接口类型提供面向对象的多态性。与Java接口或c++抽象虚类一样,Go接口包含一系列方法名称和签名。例如,io。作家中定义的接口io包装如下图2

f2.jpg
图2。io包的写入器接口。

接受一个字节片段并返回一个整数和可能的错误。与Java和c++不同,任何具有与接口相同名称和签名的方法的Go类型都被认为实现了该接口,而无需显式声明它这样做。例如,类型操作系统。文件有一个方法,因此它实现io。作家,没有像Java的“实现”注释那样的显式信号。

避免接口和实现之间的显式关联使得Go程序员可以定义小的、灵活的、通常特别的接口,而不是将它们用作复杂类型层次结构中的基础块。它鼓励在开发期间捕获关系和操作,而不是需要提前计划和定义它们。这对大型程序尤其有帮助,因为在最初开始开发时,最终结构很难清楚地看到。删除声明实现的簿记会鼓励使用精确的、一种或两种方法的接口,例如作家、读者,斯金格(类似于Java的toString方法),等等,这些方法遍布标准库。

第一次学习Go的开发人员通常会担心某个类型无意间实现了一个接口。虽然很容易建立假设,但在实践中,不太可能为两个不兼容的操作选择相同的名称和签名,我们从未在真正的围棋程序中看到过这种情况。

回到顶部

并发性

当我们开始设计Go时,多核计算机已经广泛应用,但线程仍然是所有流行语言和操作系统中的重要概念。创建、使用和管理线程的困难使它们不受欢迎,24限制了对多核cpu全部功能的访问。解决这种矛盾是创造围棋的主要动机之一。

Go语言本身包含了多个并发控制线程的概念,称为了goroutine,运行在一个共享地址空间,并有效地多路复用到操作系统线程。对阻塞操作的调用,例如从文件或网络读取数据,只阻塞执行该操作的gor例程;线程上的其他goroutines可能会被移动到另一个线程,这样它们就可以在调用者被阻塞时继续执行。Goroutines一开始只有几千字节的栈,可以根据需要调整大小,不需要程序员参与。开发人员使用goroutines作为构建程序的丰富的、廉价的原语。对于服务器程序来说,拥有数千甚至数百万个goroutine是一种惯例,因为它们比线程便宜得多。

例如,网侦听器接口是否带有接受方法,该方法可以侦听并返回新的传入网络连接。图3显示了一个函数它接受连接并启动一个新的gor例程来为每个连接运行服务函数。

f3.jpg
图3。一台Go网络服务器。

无限的在listen函数体(第22-28行)调用中循环侦听器。接受,它返回两个值:连接和可能的错误。假设没有错误,则语句(第27行)开始它的参数—函数调用服务(康涅狄格州)-一个新的gor例程,类似于Unix shell命令的&后缀,但在相同的操作系统进程中。要调用的函数及其参数在原始的gor例程中求值;复制这些值以创建新gor例程的初始堆栈框架。因此,程序运行一个独立的实例服务功能为每个传入的网络连接。一个调用服务每次处理一个给定连接上的请求(对处理(要求)在第37行没有前缀为);每个调用都可以阻塞,而不影响其他网络连接的处理。

在底层,Go实现使用高效的多路复用操作(如Linux的epoll)来处理并发I/O操作,但用户看不到这一点。Go运行库反而呈现了阻塞I/O的抽象,其中每个gor例程依次执行—不需要回调—这很容易推断。

在创建了多个goroutine之后,一个程序通常必须在它们之间进行协调。去提供渠道通道是一种单向的、有限大小的管道,在goroutines之间携带输入的消息。Go还提供了一种多路选择可以控制执行的原语,根据该原语可以进行通信。这些思想改编自Hoare的“通信顺序过程”19以及早期的语言实验,特别是Newsqueak,25Alef,31和地狱。12

图4的替代版本,用于限制任何时刻服务的连接数量。

f4.jpg
图4。一个围棋网络服务器,限制为10个连接。

这个版本的首先创建一个名为ch(第42行),然后启动10个服务器goroutines池(第44-46行),它们从单个通道接收连接。随着新的联系被接受,发送每个ch使用send语句,Ch < - conn(53行)。服务器执行接收表达式< - ch(第59行),完成通信。通道创建时没有空间来缓冲正在发送的值(这是Go中的默认值),所以在10个服务器忙于前10个连接之后,第11个Ch < - conn将阻塞,直到服务器完成服务调用并执行新的接收。被阻塞的通信操作给监听器造成了隐式的反压力,阻止它接受一个新的连接,直到它完成前一个连接的移交。

注意,这些程序中缺少互斥锁或其他传统同步机制。信道上的数据值通信同时具有同步功能;按照约定,在通道上发送数据将所有权从发送方传递给接收方。Go有提供互斥锁、条件变量、信号量和用于底层使用的原子值的库,但是通道通常是更好的选择。根据我们的经验,人们对消息传递(使用通信在gor例程之间转移所有权)的推理比对互斥锁和条件变量的推理更容易、更正确。早期的一句箴言是,“不要通过分享记忆来交流;相反,通过通信来共享内存。”

Go的垃圾收集器极大地简化了并发api的设计,消除了由哪个goroutine负责释放共享数据的问题。和大多数语言一样(但不像Rust22)时,可变数据的所有权不会被类型系统静态跟踪。相反,Go与TSAN集成28提供一种用于测试和有限生产的动态race检测器。

回到顶部

安全与安全

开发新语言的部分原因是为了解决以前语言的缺陷,就围棋而言,这些缺陷包括影响网络软件安全的安全问题。Go删除了在C和c++程序中导致许多安全问题的未定义行为。整数类型之间不会自动强制转换。空指针解引用和出界的数组和切片索引会导致运行时异常。没有进入堆栈框架的悬空指针:任何可能比其堆栈框架寿命更长的变量,例如在闭包中捕获的变量,将被移到堆中。堆中也没有悬空指针;使用垃圾收集器而不是手动内存管理消除了释放后使用的错误。当然,Go并不能解决所有问题,还有一些遗漏的东西也许应该得到解决。例如,整数溢出可能是运行时错误,而不是定义为环绕。

由于Go是一种用于编写系统的语言,它可能需要破坏类型安全的机器级操作,因此它能够强制将指针从一种类型转移到另一种类型,并执行地址算术,但只有通过使用不安全的包装及其受限制的特殊类型不安全的。指针.必须注意保持类型系统违规与垃圾收集器兼容——例如,垃圾收集器必须始终能够识别特定的单词是整数还是指针。在实践中,不安全的package很少出现:safe Go是相当高效的。看到进口“不安全”因此,这是一个信号,可以更仔细地检查源文件,以发现可能的安全问题。

Go的安全特性使它比C或c++等语言更适合用于加密和其他安全关键代码。一个小错误,比如数组索引越界,可能会导致C和c++中的敏感数据泄露或远程执行,从而导致Go中的运行时异常,停止程序并极大地限制了潜在的影响。Go附带了一套完整的密码库,包括SSL/TLS支持;标准库包括一个生产就绪的HTTPS客户机和服务器。事实上,围棋结合了安全性、性能和高质量的库,使其成为现代安全工作的热门试验场。例如,免费获得的证书颁发机构Let’s Encrypt的生产服务依赖于Go2最近又跨越了一个里程碑,发行了10亿份证书。1

回到顶部

完整性

Go在语言、库和工具级别提供了现代开发所需的核心部分。这需要谨慎的平衡,添加足够的有用的“开箱即用”,同时不要添加太多,以免我们自己的开发过程陷入支持太多功能的泥潭。

该语言将字符串、散列映射和动态大小的数组作为内置的、易于使用的数据类型。如前所述,这些对大多数围棋程序来说已经足够了。结果是Go程序之间有了更好的互操作性——例如,没有相互竞争的字符串或散列映射实现来分割包生态系统。Go包含goroutines和channels是另一种形式的完整性。这些提供了现代网络程序所需的核心并发功能。直接在语言中提供它们(而不是在库中),可以更容易地定制语法、语义和实现,使它们尽可能轻量级和易于使用,同时为所有用户提供统一的方法。

标准库包括一个生产就绪的HTTPS客户机和服务器。对于与Internet上其他机器交互的程序来说,这是至关重要的。直接填充这些需求可以避免额外的碎片化。我们已经看过了io。作家接口;任何输出数据流通过约定实现此接口,并与所有其他I/O适配器互操作。图1的ListenAndServe另一个例子是,调用需要第二个类型实参http。处理程序,其定义见图5.这个论点http.HandlerFunc(你好)实现它的ServeHTTP方法通过调用你好.该库创建一个新的gor例程来处理每个连接,就像本文“并发性”一节中的侦听器示例一样,因此处理程序可以以简单的阻塞风格编写,服务器可以自动扩展以处理多个并发连接。

f5.jpg
图5。net/http包的处理程序接口。

http包还提供了一个基本的分派器,本身是的另一个实现处理程序,它允许为不同的URL路径注册不同的处理程序。建立处理程序因为这个商定的接口允许创建许多不同类型的HTTP服务器中间件并进行互操作。我们不需要将所有这些实现添加到标准库中,但是我们需要建立允许它们一起工作的接口。

标准的Go发行版还提供了对交叉编译、测试、分析、代码覆盖、模糊化等的集成支持。测试是建立关于核心概念(例如什么是测试用例以及如何运行它)的共识的另一个领域,它支持创建良好互操作的自定义测试库和测试执行环境。

回到顶部

一致性

我们对Go的一个目标是让它在不同的实现、执行上下文,甚至随着时间的推移表现相同。这种“无聊”的一致性行为使开发人员能够专注于他们的日常工作,并使Go退居幕后。

首先,该语言尽可能地指定一致的结果,即使是错误的行为,如本文的“安全和安全”一节中讨论的空指针解引用和出界数组索引。Go需要不一致行为的一个例外是对散列映射的迭代。我们发现程序员经常不经意地编写依赖于哈希函数的代码,导致在不同的架构或Go实现上产生不同的结果。

要使程序在任何地方都具有相同的行为,一种选择是强制使用特定的哈希函数。相反,Go定义映射迭代是非确定性的。该实现为每个映射使用不同的随机种子,并在哈希表中的随机偏移处开始对映射的每次迭代。其结果是,映射在不同的实现之间始终是不可预测的:代码不能意外地依赖于实现细节。类似地,比赛检测器为调度决策增加了额外的随机性,创造了更多观察比赛的机会。


当我们开始设计Go时,多核计算机已经广泛应用,但线程仍然是所有流行语言和操作系统中的重要概念。


一致性的另一个方面是程序整个生命周期的性能。决定使用传统的编译器来实现Go,而不是Java和Node.js等语言使用的JIT,这在启动时和短期程序中提供了一致的性能:不会因为“慢启动”而影响每个进程生命周期的前几秒。如上一节所述,这种快速启动使Go成为命令行工具和扩展网络服务器(如谷歌App Engine)的一个有吸引力的目标。30.

一致性性能包括垃圾收集的开销。最初的Go原型使用了一个基本的、停止世界的垃圾收集器,当然,这在网络服务器中引入了显著的尾部延迟。如今,Go使用了一个完全并发的垃圾收集器,暂停时间不到一毫秒,21通常只有几微秒,与堆大小无关。主要的延迟是操作系统向必须中断的线程发送信号所花费的时间。

最后一种一致性是语言和库随时间的变化。在围棋诞生的头几年里,我们每周都会对它进行修补和调整。当用户更新到新的围棋版本时,经常需要更改他们的程序。自动化工具减少了负担,但手工调整也是必要的。从2012年发布的Go版本1开始,我们公开承诺只对语言和标准库进行向后兼容的更改,以便程序在使用更新的Go版本编译时可以继续运行。16这种承诺吸引了工业界,不仅鼓励了长期的工程项目,还鼓励了其他的努力,如书籍、培训课程和一个蓬勃发展的第三方软件包生态系统。

回到顶部

Tool-Aided发展

大规模的软件开发需要大量的自动化和工具。从一开始,Go就被设计为通过使其易于创建来鼓励这种工具。

开发人员对围棋的日常体验是通过命令。与只编译或运行代码的语言命令不同Command为开发周期的所有关键部分提供子命令:去建立而且去安装构建和安装可执行文件,去测试运行测试用例添加一个新的依赖项。的命令还通过提供对构建细节(例如包图)的编程访问来支持新工具的创建。

其中一个工具是去兽医,它执行增量的、每次打包的程序分析,缓存的方式与缓存已编译的目标文件支持增量构建的方式相同。的去兽医工具的目标是高精度地识别常见的正确性问题,以便开发人员习惯于注意它的报告。简单的例子包括在调用中检查格式和参数是否匹配fmt。Printf以及相关函数,或诊断变量或结构域未使用的写操作。这些不是编译器错误,因为我们不希望旧代码仅仅因为发现了一个新的可能的错误而停止编译。它们也不是编译器警告;用户学会忽略这些。将检查放在单独的工具中允许它们在对开发人员方便的时间运行,而不影响普通的构建过程。它还使所有开发人员都可以使用相同的检查,即使在使用Go编译器的替代实现(如Gccgo)时也是如此15或Gollvm。17增量方法使这些静态检查足够有效,我们可以在期间自动运行它们去测试,然后再运行测试本身。测试是用户寻找bug的时候,报告通常有助于解释实际的测试失败。这个增量框架也可以被其他工具重用。

分析程序的工具是有帮助的,但是编辑程序的工具就更好了,特别是对于程序维护来说,其中很多都是乏味的,而且自动化的时机已经成熟。

围棋程序的标准布局是用算法定义的。一个工具,go的,将源文件解析为抽象语法树,然后使用一致应用的布局规则将其格式化回源代码。14在Go中,在将代码存储到源代码控制中之前格式化代码被认为是一种最佳实践。这种方法使成千上万的开发人员能够在一个共享的代码库上工作,而不需要经常讨论大括号样式和其他伴随大量工作而来的细节。更重要的是,工具可以修改围棋程序,通过操作抽象语法形式,然后使用go的的打印机。只有实际改变的部分才会被触摸,从而产生“差异”,与人用手得到的结果相匹配。人和程序可以在同一个代码库中无缝协作。

为了实现这种方法,Go的语法被设计为在没有类型信息或任何其他外部输入的情况下解析源文件,并且没有预处理器或其他宏系统。Go标准库提供的包允许工具重新创建的输入和输出端go的,以及一个完整的类型检查器。

在发布Go版本1(第一个稳定的Go版本)之前,我们编写了一个重构工具,叫做gofix,它使用这些包来解析源代码,重写树,并写出格式良好的代码。我们使用gofix,例如,当从映射中删除条目的语法发生更改时。每次用户更新到新版本时,他们就可以运行gofix在它们的源文件上自动应用更新到新版本所需的大多数更改。5

这些技术也适用于IDE插件的构造29以及其他支持Go程序员的工具——分析器、调试器、分析器、构建自动化器、测试框架等等。Go的常规语法、已建立的算法代码布局约定以及直接的标准库支持使得这类工具的构建比使用其他方法容易得多。因此,围棋世界拥有一个丰富的、不断扩展的、互操作的工具箱。

回到顶部

在语言和工具之后,用户体验Go的下一个关键方面是可用的库。作为一种适合分布式计算的语言,在Go中没有必须发布Go包的中央服务器。相反,以域名开头的每个导入路径都被解释为URL(隐式的前导https://)给出远程源代码的位置。例如,进口“github.com/google/uuid”获取位于相应GitHub存储库中的代码。

宿主源代码最常见的方式是指向公共Git或Mercurial服务器,但私有服务器也同样得到很好的支持,作者可以选择发布静态文件包,而不是打开对源代码控制系统的访问。这种灵活的设计和发布库的便捷性创造了一个可导入的Go包的繁荣社区。依赖域名避免了急于在平面包名称空间中声明有价值的条目。

仅仅下载软件包是不够的;我们还必须知道使用哪个版本。Go将包分组成称为模块的版本单元。模块可以为它的一个依赖项指定最低要求版本,但不能指定其他约束。当构建一个特定的程序时,Go通过选择最大的依赖模块来解决相互竞争的需要的版本:如果程序的一部分需要依赖的版本1.2.0,而另一部分需要版本1.3.0,Go选择版本1.3.0——也就是说,Go需要使用语义版本控制,27其中1.3.0版本必须是1.2.0的临时替代品。另一方面,在这种情况下,Go不会选择版本1.4.0,即使它是可用的,因为程序的任何部分都没有明确要求更新的版本。该规则保持构建的可重复性,并将由新版本引入的意外破坏更改导致的潜在破坏风险降至最低。

在语义版本控制中,模块可能只在新的主要版本(如2.0.0)中引入有意的破坏性更改。在Go中,从2.0.0开始的每个主要版本都由一个主要版本后缀标识,例如/ v2不同的主要版本与其他具有不同名称的模块一样保持独立。这种方法不允许菱形依赖问题,并且在实践中它适应不兼容性以及具有更细粒度约束的系统。6

为了提高从互联网上下载包的构建的可靠性和可重复性,我们运行了两个默认在Go工具链中使用的服务:可用的Go包的公共镜像和其预期内容的加密签名透明日志。81020.即便如此,从互联网上下载的软件包的广泛使用仍然存在安全和其他风险。7我们正在努力使Go工具链能够主动识别并向用户报告漏洞包。

回到顶部

结论

虽然大多数语言的设计都集中在语法、语义或类型方面的创新,但Go专注于软件开发过程本身。Go是高效的、容易学习的、免费的,但是我们相信它成功的原因是它所采用的编写程序的方法,特别是多名程序员在一个共享的代码库上工作。语言本身最不寻常的特性——并发性——解决了2010年代多核cpu激增时出现的问题。但更重要的是早期的工作,它们为打包、依赖、构建、测试、部署和软件开发世界的其他日常任务奠定了基础,这些方面在语言设计中通常不是最重要的。

这些想法吸引了志同道合的开发人员,他们重视结果:简单的并发性、清晰的依赖关系、可伸缩的开发和生产、安全的程序、简单的部署、自动代码格式化、工具辅助开发等等。这些早期的开发者帮助普及了围棋,并为最初的围棋包生态系统播下了种子。他们还通过将编译器和库移植到Windows和其他操作系统(最初的版本只支持Linux和MacOS X)推动了该语言的早期发展。

并不是每个人都喜欢——例如,有些人反对该语言省略了继承和泛型类型等常见特性。但围棋以发展为中心的哲学非常有趣,也足够有效,在保持最初推动围棋存在的核心原则的同时,围棋社区蓬勃发展。很大程度上要感谢这个社区和它所建立的技术,Go现在是现代云计算环境的重要组成部分。

自从第一版发布以来,围棋语言几乎被冻结了。然而,工具已经显著扩展,有了更好的编译器、更强大的构建和测试工具,以及改进的依赖管理,更不用说大量支持Go的开源工具了。不过,变化正在到来:2022年3月发布的Go 1.18包含了对该语言进行真正改变的第一个版本,这是一个被广泛要求的版本——参数多态性的第一个削减。我们从原始语言中去掉了任何形式的泛型,因为我们敏锐地意识到,要做好设计是非常困难的,而且在其他语言中,它往往是复杂性的来源,而不是生产力的来源。在围棋诞生的第一个十年里,我们考虑了一些设计,但直到最近才找到一款我们觉得适合围棋的。在保持一致性、完整性和社区原则的同时进行如此大的语言更改将是对该方法的严峻考验。

回到顶部

致谢

最早的围棋工作得益于谷歌许多同事的建议和帮助。自从公开发布以来,Go一直在成长和改进,这要感谢谷歌上的一个扩展的Go团队以及大量的开源贡献者。围棋现在是成千上万人的作品,太多了,无法在此一一列举。我们感谢所有帮助Go取得今天成就的人。

回到顶部

参考文献

1.Aas, J.和Gran, S. Let's加密公司已经发行了十亿份证书。让我们加密(2020),https://letsencrypt.org/2020/02/27/one-billion-certs.html

2.Aas, J.,等。让我们加密:一个自动化的证书颁发机构来加密整个网络。在2019年ACM计算机与通信安全SIGSAC会议论文集, 2473 - 2487。

3.边缘生活:监控和运行一个非常大的Perforce装置。发表于2007 Perforce用户配置。https://go.dev/s/bloch2007

4.Chang, F.,等。Bigtable:结构化数据的分布式存储系统。在7thUSENIX操作系统设计与实现研讨会(2006), 205 - 218。

5.考克斯介绍Gofix。去博客(2011),https://go.dev/blog/introducing-gofix

6.Go中版本控制的原则。(2019),https://research.swtch.com/vgo-principles

7.Cox, R.幸存的软件依赖性。ACM的通信, 9(2019年8月),36-43。

8.Cox, R. Transparent客户日志(2019年),https://research.swtch.com/tlog

9.考克斯,r。派克,r。去编程吧。发表于谷歌I / O (2010)https://www.youtube.com/watch?v=jgVhBThJdXc

10.Crosby, S.A.和Wallach, D.S.用于篡改明显测井的高效数据结构。在十八届立法会会议记录thUSENIX安全协会。(2009), 317 - 334。

11.多诺万,匿名戒酒协会,克尼根,B.W.Go编程语言。addison - wesley,美国(2015年)。

12.多沃德,南,派克,R。和温特伯顿,p。在IEEE COMPCON 97论文集(1997), 245 - 250。

13.在个人计算机Lilith上用Modula-2单独编译和Modula-2编译器的结构。博士论文。瑞士联邦理工学院(1983),https://www.cfbsoftware.com/modula2/ETH7286.pdf

14.杰兰德:去fmt你的代码。去博客(2013),https://go.dev/blog/gofmt

15.项目。设置和使用gccgo。(2009),https://go.dev/doc/install/gccgo

16.项目。围棋1和围棋程序的未来。(2012),https://go.dev/doc/go1compat

17.项目。Gollvm,一个基于llvm的Go编译器。(2017),https://go.googlesource.com/gollvm/

18.项目。Go编程语言规范。(2021),https://go.dev/ref/spec

19.霍尔,C.A.R.通信顺序进程。美国Prentice-Hall公司(1985)。

20.模块代理:查询的生命。发表于GopherCon 2019https://www.youtube.com/watch?v=KqTySYYhPUE

21.哈德森,R.L. get to Go: Go的垃圾收集者之旅。去博客(2018),https://go.dev/blog/ismmkeynote

22.S. Klabnik和C. Nichols。Rust编程语言。No Starch Press,美国(2018)。

23.Lam, A.为Bazel使用远程缓存服务。ACM的通信, 1(2018年12月),38-42。

24.为什么线程是一个坏主意(对于大多数目的)。(1995),https://web.stanford.edu/~ouster/cgi-bin/papers/threads.pdf

25.关于Newsqueak的实现。软件:实践与经验, 7(1990), 649-659。

26.Pike, R., Dorward, S., Griesemer, R., and Quinlan, S.解释数据:与Sawzall并行分析。科学程序设计(2005), 277 - 298。

27.语义版本2.0.0。(2013),https://semver.org/

28.Serebryany, K., Potapenko, A., Iskhodzhanov, T.,和Vyukov, D., LLVM编译器的动态竞态检测:ThreadSanitizer编译时检测。在运行时验证库尔希德(S. Khurshid)和森(K. Sen)。柏林,海德堡,柏林,海德堡(2012),110-114。

29.斯坦布勒,r,去吧,请别打我的编辑。发表于GopherCon 2019https://www.youtube.com/watch?v=EFJfdWzBHwE

30.Symonds, D., Tao, N.和Gerrand, A. Go和谷歌应用程序引擎。去博客(2011),https://go.dev/blog/appengine

31.Winterbottom, P. Alef语言参考手册。在计划9:程序员手册第2卷。Harcourt Brace and Co.,纽约(1996)。

回到顶部

作者

拉斯考克斯rsc@go.dev),作为软件工程师在美国加州山景城谷歌创立了围棋编程语言和环境。Cox, Griesemer和Taylor继续领导谷歌的围棋项目,而Pike和Thompson已经退休。

罗伯特Griesemer作为软件工程师在美国加州山景城的谷歌创建了围棋编程语言和环境。Cox, Griesemer和Taylor继续领导谷歌的围棋项目,而Pike和Thompson已经退休。

罗伯•派克作为软件工程师在美国加州山景城的谷歌创建了围棋编程语言和环境。Cox, Griesemer和Taylor继续领导谷歌的围棋项目,而Pike和Thompson已经退休。

伊恩·兰斯泰勒作为软件工程师在美国加州山景城的谷歌创建了围棋编程语言和环境。Cox, Griesemer和Taylor继续领导谷歌的围棋项目,而Pike和Thompson已经退休。

Ken Thompson作为软件工程师在美国加州山景城的谷歌创建了围棋编程语言和环境。Cox, Griesemer和Taylor继续领导谷歌的围棋项目,而Pike和Thompson已经退休。


cacm_ccby.gif本作品授权于http://creativecommons.org/licenses/by/4.0/

数字图书馆是由计算机协会出版的。版权所有©2022 ACM股份有限公司


没有发现记录

Baidu
map