纯粹业余兴趣翻译,能力有限水平业余,欢迎拍砖。
文中还有一些句子没作翻译,是因为我完全没弄懂:-)
问题较多, 分part翻译,本文为第一part。
原文: Frequently Asked Questions (FAQ)
起源
What is the purpose of the project?
非系统底层语言的出现已经有十多年的时间, 而在这期间计算环境已经发生了巨大变化。以下是几大发展趋势:
- 计算机运算速度有极大提升, 但是软件开发效率没有明显提高。
- 依赖管理是今天软件开发一个重要部分, 但像C语言中的“header files”阻碍了简单的依赖分析和快速的编译。
- 与类似Java, C++等带有重类型系统的语言相比, 弱类型语言编程的兴起促使越来越多的开发者使用诸如Python或JavaScript这些动态类型语言来进行编程。
- 像垃圾回收和并行计算这些基础概念在当前流行的系统语言中并没有得到很好的支持。
- 多核计算机的出现引起了更多的担忧和疑惑。
我们相信值得去尝试开发一种支持并发、垃圾回收并且能被快速编译的新语言。对于以上的几点:
- 能够在一台单机上快速编译(如几秒内)GO的一个大项目。
- Go提供了这样一个软件开发模型: 使依赖分析更简单, 而避免了类似C那样在文件和库上使用依赖的不便。
- Go的类型系统没有层次结构, 所有没有把时间花在定义类型关系上。同样, 虽然Go有静态类型, 它也尽量使类型显得更加轻量(相对于其他经典Object-Oriented语言来说)。
- Go提供了完整的垃圾回收机制, 并且在并发执行和通讯方面提供了基础支持。
- 基于自身设计, GO在多核机器上的软件系统的构建给出了方案。
关于这个问题,更详细和有价值的答案可以参考这篇文章 Go at Google: Language Design in the Service of Software Engineering。
What is the status of the project?
Go在2009/11/10时成为一个开源项目。之后经过几年的积极发展, 在2012/3/28这天Go1.0稳定版本正式发布。该版本包含了语言规范、标准库、 常用工具库, 为创建可靠的企业项目提供了基础。
有了稳定的基础, 我们使用Go来开发程序, 项目以及一些工具, 而不是频繁的换语言或者库。实际上Go1的目标是提供长期稳定的开发环境。Go 1上的任何更改都是向后兼容的。我们要清楚将来的GO应该发展成什么样子,而不只是改改版本号。
当然, Go会不断改进, 但方向侧重在性能, 可靠性, 可移植性以及一些新功能的开发, 比如完善国际化支持。
将来可能有Go 2版本, 不过目前几年主要是发展Go 1(译注: 截止目前, 最新为Go1.6, 于2016-01-27正式发布)。
What’s the origin of the mascot?
吉祥物的logo是由Renée French来设计的, 他同时还设计了Glenda, the Plan 9 bunny。地鼠是从她几年前穿的WFMU T-shirt获得灵感而设计的。该logo在Creative Commons Attribution 3.0许可内使用。
What is the history of the project?
Robert Griesemer, Rob Pike和Ken Thompson在2007年9月份变开始在白板上描绘一个新语言的目标。 很快目标便被敲定,而该语言有了最初的轮廓,并且在大家的业余努力下渐进发展。到了2008年1月份, Ken开始着手设计输出结果为C语言的编译器,用以探索思路。到了年中,Go已经成为一个全职项目,并且有充足准备来开发其编译器。在2008年5月, independently started on a GCC front end for Go using the draft specification(??)。 后半年加入的Russ Cox, 通过原型开始实现Go的语言规则及一些基础库。
Go在2009-11-10成员开源项目。社区中的众多开发者贡献了很多建设性建议及讨论, 还有代码。
Why are you creating a new language?
Go之所以诞生, 是对现有语言和系统编程环境不满的一种驱动释然。编程变得如此困难, 而语言的选择也变得迷茫。高效编译, 高效执行, 易于编程这三者在同一个主流语言中不可同时兼得。开发者被动选择了动态类型语言(如Python/JavaScript)以进行安全有效的开发,而不是C++或Java。
Go尝试既做到解析型、动态类型语言那样易于开发, 也做到静态、编译语言一样高效安全。同样它侧重支持网络及多核编程。
最后, 希望通过Go来加速编译: 在单台机器上最多需要几秒钟时间来完成一个大项目的编译流程。要达到这些目标,需要解决大量这样的问题: 一个富于表达而且轻巧的类型系统;并发和垃圾回收等等。现有的库或者工具并不能很好的解决这些问题, 因此新的语言应运而生。
Go at Google阐述了Go语言诞生的背景以及动机,通过它可以找到更多有关这个问题的答案。
What are Go’s ancestors?
Go很大程度上属于C家族(基础语法),参考并吸收了Pascal/Modula/Oberon这些语言的优点(declarations, packages),结合Tony Hoare的CSP理论(如Newsqueak和Limbo)做了一些改进。然而它是一个全新的语言。Go语言设计时都在考虑开发者可以做什么以及怎么做, 至少像我们一样更加有效率, 也意味着更有乐趣。
What are the guiding principles in the design?
现在的编程包含了太多的记账, 重复和文档性工作。
如同Dick Gabriel所说, 阅读老的程序就像一个善言的研究员和一个善于学习的机器同事之间的一个安静的对话,而不是和编译器之间的一场争吵。但谁会想到诡辩会带来噪音呢” sophistication(复杂化?)是有原因的—谁也不想回到老的语言—但它是否能更平和的实现呢?
Go尝试减少输入的代码数量。 透过它的设计, 我们尝试减少混乱和复杂度。在Go中, 没有前置声明和头文件;所有对象/变量都是只声明一次。
初始化方式易读, 自动化而且易用。语法干净, 关键字轻量。可以使用 := (声明并定义)的结构来简化普通表达式: foo.Foo* myFoo = new(foo.Foo)。而最基本的, Go的类型没有层次结构, 也就不需要声明类型之间的关系。这种简化机制使得Go轻松做到富于表达,易于理解。
另外一个重要的原则是保持概念正交。可以实现任何类型的方法;
结构体用来表示数据而接口用来表示抽象;等等. 正交性使得组合操作时更容易清楚其中发生了什么。
使用
Is Google using Go internally?
是的。在Google内部已经有数个Go项目部署在生产环境。一个对外的例子为golang.org网站的后台服务。 它是运行在Google App Engine上生产环境中的一个godoc相关服务。
其他例子像Vitess(用于large-scale SQL installation), dl.google.com(google下载服务, 提供了chrome二进制文件以及其他一下大体积安装包)。
Do Go programs link with C/C++ programs?
Go有两种编译器, gc和gccgo。gc使用了一种不同的calling convention(Calling Convention是指程序在函数调用时传递参数和获取返回值所采用的方法:通过寄存器、或通过栈、或者是两者的混合。)和链接器, 因此只有C程序通过相同的calling convention可以链接;是C编译器而不是C++编译器。gccgo作为gcc的前端, 能够链接到GCC编译的C/C++程序。
cgo程序提供了一种”外部函数接口”机制,使得在Go代码中安全调用C库。
SWIG拓展了C++库以支持外部调用。
Does Go support Google’s protocol buffers?
有一个独立的开源项目, 提供了必要的编译组件和库。地址:https://github.com/golang/protobuf/
Can I translate the Go home page into another language?
当然。我们鼓励开发者用他们的语言去构建Go语言相关网站。
然而如果选择添加google logo或者brand到你的网站(在golang.org上没有出现的), 那么就需要遵守 www.google.com/permissions/guidelines.html上声明的准则。
设计
What’s up with Unicode identifiers?
从ASCII的范围拓展标识符的空间, 这是很重要的。Go中的rule-indentifier字符必须是用Unicode定义的字母或数字-简单易懂,易于实现,但是有限制。组合标识符在当前来说是不在设计之内的。Until there is an agreed external definition of what an identifier might be, plus a definition of canonicalization of identifiers that guarantees no ambiguity, it seemed better to keep combining characters out of the mix. Thus we have a simple rule that can be expanded later without breaking programs, one that avoids bugs that would surely arise from a rule that admits ambiguous identifiers.
与此相关,由于一个导出的identifier必须大写开头, 一些由某种或某几种创建的identifier并不能导出使用。目前唯一的解决方法是使用类似X日本语
的方式, 但这显然很挫; 我们正在考虑其他的方案。而大小写与可见性相关(case-for-visibility)的规则不太可能会改, 因为这是Go最常用的一种特性了。
Why does Go not have feature X?
每一种语言都会包含一些新的特性, 而缺少某些人一些最喜爱的特性。Go的设计着重于编码快乐感, 编译的速度,正交原则,以及支持一些像并发执行和垃圾回收这样的特性。你最喜爱的特性可能在Go里没有, 可能是因为它并不适合, 也可能是它影响了编译性能或设计的整洁度, 又或者它使得基本系统模型变得复杂。
如果Go缺失某些特性而影响了你的开发,请原谅我们并研究Go中所包含的特性。你可能会发现一些有趣的弥补方案。
Why does Go not have generic types?
泛型可能会在某个时候加入。我们并不认为这很紧要,虽然我们也知道有些开发者迫切需要。
泛型提供了便利,但它们会在复杂度上增加类型系统及run-time的成本。
尽管我们一直在思考,但目前还是没有找到一种设计可以让价值与复杂度成正比相关。同时, Go中内建map和slice,增强了使用空interface去构建容器的能力(通过显式拆箱);这意味着很多时候可以这样编码已达到使用泛型的效果。
这里保留一个开放问题,可以从the generics proposal issue获取更多信息。
Why does Go not have exceptions?
我们认为将类似try-catch-finaly这样的异常处理耦合到控制结构中的话,
会产生令人费解的代码。这也往往鼓励开发者去标识过多的一般错误,比如打开文件失败。
Go采取了不同的处理方法。对于一般的错误处理, Go的多值返回可以在不用返回结果的情况下报告一个错误。带上Go其他特性的一种典型错误类型, 可以使得错误处理很轻快, 而有别于其他语言。
Go也有一对内建方法,支持发生异常时发送信号和从异常中恢复。恢复机制仅在方法某部分发生错误而状态异常后执行,这足以处理灾难而不需要额外的控制结构。用的好的话可以让错误处理代码部分更整洁。
更详细内容请参考Defer, Panic, and Recover
Why does Go not have assertions?
Go不支持断言。使用断言不可否认的方便,但我们认为很多人是用了断言后忽略怎么正确的处理和报告错误。正确的错误处理意味着在非致命错误发生后程序应该继续执行而不是崩溃。正确的错误报告也意味着错误精确发生的位置,保存了用于分析的错误堆栈信息。精确的错误是很有必要的,尤其是开发者并不熟悉当前的代码。
我们明白这是争论的焦点。在Go中, 很多东西和现代实践理论不同,仅仅是因为我们觉得有必要尝试不同的实现。
Why build concurrency on the ideas of CSP?
并发和多线程编程是公认的有难度。我们认为这和复杂的设计(如pthreads)分不开, 和过度强调底层细节也有关, 比如mutex, condition变量,memory barrier。使用上层接口可以使代码更简单, 即使也有类似锁这些底层概念。
一个用于并发提供高层次语言支持的最成功的模型来自Hoare的Communicating Sequential Processes, 简称CSP模型。 Occam和 Erlang这两种语言也使用了CSP模型。 Go的并发原型是从该模型的另一个、channel作为第一类对象的部分体系中派生。从早期几种语言的使用经验中可以表明, CSP模型可以很好地结合到程序语言框架中使用。
Why goroutines instead of threads?
Goroutine是更容易实现并发编程的一种原因。这个想法已经存在一段时间了,就是在一组thread中多路复用、独立执行多个functions。当一个coroutine阻塞时,像调用了阻塞式的系统调用, go运行时会在同一个系统线程上自动将该线程上其他的coroutine移动到另外一个线程上面执行, 因此不会出现阻塞。程序员是看不到这个变化的, 这就是关键。结果就是, 调用coroutine会变得很廉价: 他们在栈上只有很小的开销,通常只占几KB内存。
为了使栈更小, Go运行时使用了可变的有边界的栈。一个新建的goroutine拥有几KB空间, 基本是足够使用的。Go运行时会自动增加用于存储栈的空间,从而使得goroutine有足够可用的内存。平均每次函数调用将产生cpu三次微指令的开销。实践中在同一个地址空间中同时可以创建上万个goroutines。如果goroutine就是thread,那更少的数量就能耗光系统资源。
Why are map operations not defined to be atomic?
经过长时间的讨论之后, 我们决定map的典型用法不要求在多个goroutine情况下保证安全存取(这些情况是指map为已同步的大数据集或者计算的一部分)。而要求所有map操作加锁的话, 会减慢程序的速度, 而安全性并没有明显提高。这并不是一个容易的决定,因为一个失控的map访问可能会搞垮整个程序。
The language does not preclude atomic map updates(Go并不排除map的原子更新??).
When required, such as when hosting an untrusted program, the implementation could interlock map access(在需要原子更新时, 如在执行一个不受信任的程序时, 这个实现(原子操作)可以锁住map的访问??).
Will you accept my language change?
开发者经常建议改进该语言-邮件列表里面包含了诸如此类内容的历史-但是很少建议能够得到采纳。
虽然Go是一个开源项目, 它还是受兼容性保护的, 这样可以避免因为更改而破坏现有的项目程序。如果你的建议违反了Go 1.x的规范,我们是不能接受这个想法的, 即使它很优秀。将来的主要release版可能不会兼容Go 1.x, 但是我们还不打算讨论它未来的样子。
即使你的建议符合Go 1.x规范,它也不一定符合Go的设计宗旨。参考这里,Go at Google: Language Design in the Service of Software Engineering阐述了Go的起源和它的设计的背后动机。
类型
Is Go an object-oriented language?
Go可以说是,也可以不是OO语言。虽然Go有类型和方法来进行OO编程, 但是没有类型层次。Go中interface的概念提供了一种截然不同的易用而且在某些方面更通用的方法。也可以将一个类型嵌套到其他类型,以达到相似不尽相同的子类效果。而且,Go中的方法比在C++或Java中更普遍:可以给任何类型的数据定义方法,即使是像integer这样的内建类型; 而不仅限于结构或类。
同样由于缺少类型层次结构, 使得object在Go中比其他诸如C++/Java的语言显得更加轻便。
How do I get dynamic dispatch of methods?
唯一可以动态调度方法的途径是通过接口。在struct或者其他具体类型上的方法总是通过静态解析的。
Why is there no type inheritance?
OO编程, 至少在一些主流语言中, 涉及到太多关于类型关系和关系可以自动派生的这些讨论。Go采用了一种不同的方法。
与要求程序员前置声明两个类型之间的关系不同, Go中的某个对象只要实现了某接口的所有方法,则该对象就自动实现了该接口。减少了显式声明关系的这些负担,这个方法显得更加有优势。对象类型可以同时实现多个接口, 而无需承担传统多重继承的复杂性。interface可以很轻量-接口可以只有一个甚至没有方法。Interfaces can be added after the fact if a new idea comes along or for testing—without annotating the original types.因为在对象和接口之间并没有显式的关系绑定, 也就不需要去管理类型层次结构。
通过这些idea, 可以构建一些类似类型安全的Unix管道的事情。参考一下,例如fmt.FPrintf如何格式化输出任何数据, 而不仅仅是一个文件; 又例如bufio包如何从file I/O中完全独立出来; 又或者image包如何生成压缩的图片文件。所有这些想法从一个interface(io.Writer, 其实就一个方法Writer)衍生而来。而这还只是表面上的影响。Go的interface对整个程序结构产生了深远的影响。
这需要一段时间去适应, 而这种类型隐式依赖的风格是Go最有建造力的一个特点。
Why is `len` a function and not a method?
我们也讨论过这个问题,最终还是决定将len实现为一个function而不是method, 这样不会让基础类型的接口(在Go意义上)问题复杂化。
Why does Go not support overloading of methods and operators?
如果不需要做类型匹配, 函数调度将很简单。使用其他语言的经验告诉我们,能使用多个重名方法而参数或签名不同时偶尔有用处, 但在实践中也会让人迷惑。只通过名字匹配,并且要求类型一致, 在Go系统中是主要的一种简化决策。
对于运算符重载,似乎是很便利, 但并无必要。没有它们事情会变的更简单。
Why doesn’t Go have “implements” declarations?
Go中一种类型要实现一个接口, 只要实现该接口的所有方法就可以了, 无他。这种机制使得接口可以自由定义和使用, 而不必修改现有的代码。这种类型结构化促进了关注点分离, 而提高了代码复用,更易构建一种代码开发模式。接口的语义是Go之所以自称灵活、轻盈的主要原因之一。
详情请参考type inheritance
How can I guarantee my type satisfies an interface?
你可以询问编译器来验证类型T是否实现了接口I, 如使用给类型T或类型指针T赋予零值:
type T struct{}
var _ I = T{} // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.
如果T或T并没有实现接口I, 在编译时便会报错。
如果你希望interface的使用者显式声明实现了某接口, 你可以添加一个名字有此含义的方法到该接口的方法集中。比如:
type Fooer interface {
Foo()
ImplementsFooer()
}
然后该类型必须实现Fooer的方法ImplementsFooer,在godoc中注明此处实现。
type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}
大多数的代码并没有使用这样的制约, 因为它有点费解(limit the utility of the interface idea)。然而有时候这样可以解决由于类似的接口引起混淆的问题。
Why doesn’t type T satisfy the Equal interface?
如下所示,可以通过interface的方法给对象进行比较(同类型不一定同值):
type Equaler interface {
Equal(Equaler) bool
}
及类型T:
type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler
与多态系统中的大多数情况不同,T并没有没有实现接口Equaler。T.Equal的参数类型是T, 而不是要求的类型Equaler。
在Go中,类型系统没有自动推导Equal的参数; 这个责任在开发者身上,可以自己通过定义类型T2来实现接口Equaler:
type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler
虽然这和其他类型系统不一样,但Go中任何只要实现了Equaler的类型都可以作为参数传递到T2.Equal, 而在运行时需检查该参数是否为T2类型。一些语言会在编译期间便保证了这个类型一致。
另一种相关的例子:
type Opener interface {
Open() Reader
}
func (t T3) Open() *os.File
在Go中, 这个类型T3并没有实现接口Opener。
在这种情况下, Go类型系统为程序员做的事情的确不多,而缺少子类型的机制使得实现接口的规则变得很简单: 函数的名字和参数签名是否和interface中定义的一模一样?Go的规则也很容易被有效的实现。我们觉得这些优点可以弥补Go在自动类型推导上的缺陷。是否在某一天Go应该采用多态类的形式,我们期待有一种方式可以表达这些例子的想法,并且能够进行静态检查。
有疑问加站长微信联系(非本文作者)