[翻译]Go编程语言,或者:为什么除了它,其他类C语言都是垃圾(4)

继续前文的翻译。我本以为分成 3 段就可以完成翻译的,现在看,保守估计得 4 段了。保佑德国佬别再想起点什么加进去……

———————–翻译分割线———————–

Go编程语言,或者:为什么除了它,其他类C语言都是垃圾

[翻译]Go编程语言,或者:为什么除了它,其他类C语言都是垃圾(1)

[翻译]Go编程语言,或者:为什么除了它,其他类C语言都是垃圾(2)

[翻译]Go编程语言,或者:为什么除了它,其他类C语言都是垃圾(3)

缺失的东西

有一些东西没有进入 Go 中,而我确实很想念它们。我希望社区能找到将那些东西加入进去的办法,当然得保持 Go 轻量的形式。我期望看到许多不怎么重要的功能,而有些仅仅是愿望,只有一个是我真正想要的,那就是元编程。

这不是在讨论泛型和模板。接口或多或少可以代替这些,就像上面已经提到的那样,现在来说鸭子类型是不完全的替代。

现在讨论的是真正的代码生成代码的那类。设想一种可能,实现一个特定领域语言。如果用 SQL,无论如何 SQL 将会成为代码的一部分。SQL 包可以提供一种编写 SQL 的解决方案,编译时语法和类型检查。这可能会增加编译时间,但是我宁可选择对我所有的代码进行静态检查,而不是在运行时发现 SQL 中有语法错误(还有每次调用额外增加的解析时间,而不是一次的)。

这种灵活的机制同样可以让语言得到改进,让试验性的功能及早得以实现,在考虑将其加入语言核心之前就已经稳定了。当然,得是一个良好设计的实现。没有人想重现 C/C++ 的悲剧。整合在一起,Go 可以获得强大的表达能力。

操作符/方法重载也可能提供类似的便利,因此算上这些。有特别命名方法的 Python 模型作为例子,它有恰当的语义并解决实际问题。

不幸的是,这些已经一次又一次的讨论过了,对于像我这样的新人,已经有太多没有结果的讨论,从而无法看到如何解决。引用其中一个:

> 欢迎反馈。

搜索列表中关于这些话题的内容,每个话题都能找到 1000 封以上的邮件。

这是麻烦。如果一个确定的话题已经讨论了如此多的次数,为什么官方文档里没有关于这个的任何内容?像这样的争论会阻止潜在的贡献者。同时,已经参与讨论的人也会疲于回复。

最好是社区有一个提议流程(像 Python、Java、Perl6、XMPP 等等那样)可以跟踪这些建议,标识出某个提议需要满足的需求,并认真考虑,同时汇总当前的开发状态。这样,想法就可以量化,可以通过合理的争论予以拒绝,可以用更好的想法来代替,最终使其进入语言而不用向设计目标妥协。从良好的拒绝文档中,潜在的贡献者可以学习到需要避免什么、不期望什么,以及需要做什么。

这不需要民主。对于项目管理来说乱哄哄的做法和独裁的做法成功的一样多,而失败的也一样多。最重要的是有这么一个流程,并且是透明和容易理解的。

总结来说,别落下坏印象。社区不应当是固执与傲慢的。他们会听取意见。这与另一个人在邮件列表上说的类似:

当在添加功能的时候加了麻烦,就永远不能把它拿出来了。

我完全同意这点。我需要元编程,它是如此不可思议的灵活,而它必须在 Go 强大力量控制之中。而不是像不够格的 PHP 做的那样。

开放问题

这些问题现在对于我来说有些不清晰。当我找到问题的答案时,我会更新这个综述。其中大多数是假设,其他是基于兴趣。自己判断。斜体内容是我自己对获得的答案的总结,除非有必要,没有任何引用内容。

内联能否工作?夸包的内联是如何工作的?
还不行,但是已经在处理编译器了。夸包的内联容易实现,因此 C++ 的“头文件中的代码”的混乱可以终结了。
我估计 6g/8g 会支持这个。不清除 gccgo 是否也能够有夸包的内联。

让 Go 支持 8 位可行吗?C 可以灵活扩展到 8 位微处理器。最低的 Go(受限的) 运行时硬件环境是怎样的?
当前编译器不支持。32 位是最低要求。
我希望有人能在什么时候将 Go 移植到更低要求的机器上。这可能要花点时间,C64 也不是一夜之间就有了 C 支持。^_^

Go 目标文件是否可以在运行时加载?在这些文件中的类型实现能否无缝的与宿主应用整合?
还不行,但没有任何根本原因表示为什么不行。这些可能已经在线路图上了。

那么优化的限制呢?例如 gccgo,它是否有着与编译 C++ 一致的优化级别,或者还有什么重要的步骤没有支持?
对于 6g/8g,作为编译器的标准实现,优化器还有许多缺陷,但是已经在改进中。gccgo 没有任何相关信息。
这其实很好。编译器不是最大的缺陷,但是 Go 的性能仍然很好。Go 的设计使得容易进行静态优化,这也是为什么有意限制功能的原因。看起来,应该会提供这样的功能。

可能检测作为一个众所周知的安全问题的整数溢出吗?通过运算符重载,我猜测实现一个保护数据类型的会比较容易。但是,这可能值得作为语言的核心出现,作为一个能够接受的性能下的实现。C# 已经做到了这点。
还不是时候。Go 明确的定义整数运算是被环绕的(译注:0xffffffff + 1 = ?)。C 是没有这样的定义,因此 C 编译器如果想的话可以做边界检测,而 Go 编译器不能。
这意味着语言规格定义为了适应这点可能在某些情况下会发生改变。在我看来,默认有保护的整数是值得的,而环绕的整数则作为可选的数学技巧。

接口的内存开销有多少?结构的呢?数组的呢?
简单来说,就是最理想的情况。对于每个类型都有类型信息,一个接口值需要两个指针,除此之外没有任何开销。

接口方法调度的时候,CPU 时的开销有多少?创建 goroutine 呢?调度 goroutine 呢?
接口方法的调度与 C++ 虚函数类似。创建 goroutine 仅仅分配一些内存。调度 goroutine 实际上只是简单的循环调度。因此它非常快,但是在某些工作情景下可能工作得不好。

有什么办法向不是定义在当前包的类型增加方法?例如向 int64 增加方法?
不能。绝对不能。只有定义类型的包可以添加方法。因此,没有混合。这是保证代码用途的清晰的一种内部机制。否则程序的声明周期会依赖于包的导入时机,一旦有动态加载时,就没有什么是确定的了!

是否可能向接口增加方法,也就是说一个有着接口作为接收者的方法,可以用于任何实现这个接口的对象?
不能,同样的原因。对特定类型编写一个包函数来代替这个操作。这样函数有确定的命名空间,它们不会变成魔鬼。

关于调度 goroutine:一个 goroutine 通过死循环能让其他 goroutine 以及 main 程序饿死吗(译注:不给吃,不给穿,将你们全部都榨干……),或者有什么优先级作为安全保证?
这里没有安全保证。goroutine 并不是为了替换线程的低延迟的并发/并行进程。

一方面,这很不幸。另一方面,协作式调度的复杂度更低,并且有更好的吞吐量。看看 Linux 内核有多少个调度中的循环得到了最佳的性能。低延迟,可扩展,不会资源枯竭,并且公平的优先级是难以实现的,而且那样的话就抛弃了操作系统的便利。可能最聪明的做法是增加一些将 goroutine 转化为隐含的线程的办法。

那么关于垃圾回收呢?它对于处理大量的小对象如何?
关于内存算法与 malloc() 一样有效率。当许多对象只是通过它们内部的指针引用的时候,速度可能会下降(例如,slice 就有类似的情况)。
主意,当前的是暂停世界的收集器。马上会有替代。没有什么阻止使得可以去掉基础的 GC 。让语言本身尽可能的优化有更高的优先级。

反馈

已经有许多反馈了,所以我增加了这个小节。这记录了一些不适合于我整篇内容的有趣的附注。附注让我原来的文字更清晰,那些让我觉得“是的,当然,我怎么能丢弃这些呢?”的已经整合进了上面的文字中。

三元运算符

一个读者详细介绍了 Python 的三元运算符 if foo else b 的历史。除了这个 python 已经具有高度可读的扩展性,考虑为什么增加这个结构就得往前追溯。

看起来 Python 有很长一段时间没有这个,而人们与富布尔快乐的生活在一起:some_value or “default-value”。直到有人对 condition_to_test and value_if_true or value_if_false 的做法有了主意以后。从此它走了下坡路。这个改造有缺点,于是其他改造被发明出来。每个都比之前一个要丑陋,最终,Python 有了三元运算符。

这对于 Go 有什么意义吗?当然,如果你不能打败它,那就请拥抱它吧。

D(译注:嘿,之前好像有人说:为什么不谈谈D……)

我有不谈论 D 的好理由。我并不是忘记了它,有人没有发现我那关于 D 的几个字(译注:原文是 6 个词,翻译后……)的总结。我宁可不体验它。我不写关于 D 的东西,因为我不用 D 写东西。因此,现在我应大众要求,这是无知的我对于 D 的观点:

当我第一次遇到 D (在 heise.de 新闻里,你可以在这里找到英文版本),我相当惊喜。终于有一种语言尝试与 C/C++/Java 不同了。但是当略读了文档之后,我失去了兴趣。

为什么?很简单,它只是隔靴搔痒。是的,它在一个语言里提供了所有东西,在 C++ 中用模板的实现,还有在 Java 中那些放到另一个抽象层的东西,但是这就是关键:在哪,做啥。在那些语言里可能不怎么有效率,但是映射到 D 以后相当的奇怪。

虽然统一格式的全功能语言很有趣,我却看不到它哪里适合我。它没有尝试精简。也没有提供任何值得探索的新概念。它只是无法满足我的需要,所以为什么我要切换并,并且激战于无法同他人分享的代码;无法作为开源发布,并尽早使其尽早公开使用的代码;无法同他人的代码简单的进行接口的代码?

如果我工作于一个有许多特别之处的大项目,我会想要选择 Java 或者 C++,这基于哪个语言能最好的匹配项目的需求,尤其是外来代码。D 恰好没有准备好这个。如果是小项目,对我来说 D 没有任何特别之处。这没有切换的动力。补充一下,我不喜欢自动引导或将所有东西都加入语言中。这是个企业级的语言,但是 Java 和 C# 也是企业级的。

你知道什么东西有趣?我开始拒绝 Go 的原因就是因为 D。我阅读关于 Go 的时候看到了类似的东西,然后我想“哦,好吧,这又有另外一个……”然后就忽略了新闻。当我再次读的时候,一些事实(我不记得是什么了)激发了我的好奇心,而两周后我写了这个综述。

Go 实际上是不同的。它给出了切换的理由。它切合了我的需要,并且提供了一些其他通用语言无法给与的东西。不,Haskell、Erlang(译注:又见 Erlang……)等等都不行。我喜欢从科学的角度来看待它们,但是我仍然喜欢在疲倦的时候仍然能明白自己的代码,同时我也喜欢让其它人能够明白我的代码。但最重要的是,Go 与外部世界交互很好,这要感谢 gccgo,即便它仍然是一个相对未知的语言,我应当能从使用它中获益。

在互联网上的本文

感谢互联网,为所有我未能留意到的和已经了解的讨论。这里已经有许多不同方面的思考,我发现十分有趣。你们没有让我失望。这不像你将要读的内容,但是也差不多,一些回复并没有涵盖在当前的更新中:

许多人遗漏的一个非常重要的事情是编程语言是一个工具。曾几何时,我沉迷于 Perl 5,花时间学习了所有的奇技淫巧。但是你知道这其中真正的原因吗?不是语言自身的魅力让我着迷(这是一个很小的原因)。部分原因是可以摆弄那些元素,这是来自我黑客之心的呼唤。但是真正的原因是包文件和特定的途径只需要你编写代码。这使得我可以用更少的时间解决实际问题。

这是为什么我用 C 编写 AVR、用 PyQt 处理 GUI、在 GPU 上用 ARB顶点程序汇编代替 GLSL、用 C++/Fltk 处理轻量的 GUI、Lisp 在 Emacs 上、在用 Perl 写了原型后,用 C 编写自制的车载 MP3 播放器、基于 ARM 的 Python 反编译器,当然还有,无须维护的 PHP 的 web 服务。我恨 PHP,但是它对于那个任务来说是个正确的工具。因此,攻克它并且开始为你的任务提供生产力,审查整个构思,并持续的审查下去。

这就是我对 Go 的期望,并且看起来它确实能满足这些。我看过许多事情随着时间的迁移而走向错误的方向,没有谁、没有什么事是有保证的。但是我通常选择早期选择,长期采用。并且做事就像这里这样,做到能做到的最好。我能说的就是那些其他语言不能提供给我的这一切值得继续。

有一个令我相当好奇的更好的语言设计暗示,经常是这样的句子“…除了 20 以上的研究。”模糊的建议仅仅对那些众所周知的概念有效,而不是特定话题。欢迎任何人,谁能给我指出这结果和 Go 之间的关系,以及什么匹配了设计目标的约束。在顶部有我的邮件地址!(译注:Jörg Walter)

如果只对编程和 Go 感兴趣,你可以跳过这段。下面这段是跑题的扯淡。

对于那些觉得我站在你们脚趾上咒骂的人来说:这是咒骂。意味着这不是完全客观的。它使用了讽刺和夸张的手法。一些人理解了,还有一些不理解。通常,那些理解这个的人都与我有着相同的经历。而这些都是一种公开的发泄。我仍然热爱 C,我也仍然在使用 C++ 和 Java,当它们对于任务来说是正确的工具的时候,但这不意味着它们从那以后让我这颗程序员的心快乐(译注:那些传统卫道士快乐吗?那些装逼侠快乐吗?还有那些一无所知的中国特色的老学究快乐吗?他们都不是程序员啊!)。

顺便说一下,“某些东西并不适合另一些事情”与“用某些东西不可能成功做另一些事情”之间应该还是有不同的,即便它们相伴出现了 10000 次。

两个告诫我说不要让 OO 脱离并发的朋友,有什么细节说明吗?我没办法找到这个误解的本质,除了一些简单的说明外。接受过专业训练的读者能很好的理解真正的细节,而没有理论背景的人只能有我说的那个宏观思路。

对于被我关于 Perl6 的开发时间戳到的 Perl6 爱好者看这个:毫无疑问,通常来说我是一个 Perl 的爱好者。在我的机器上装有 Rakudo Star,并为了看看能有什么好处而认真的尝试过用其编写小工具。就像我用 Go 做得那样。然而没有收获,Perl6 还没准备好用于生产环境,不过我得承认,它快达到了。

对于我咒骂的分类和选择的语言吹毛求疵的人看这个:当然我只能讨论我知道的。这里确实有说是咒骂 C 族或者 Algol 后即语言。在介绍中我特别提到若干种其他语言已经是某些系统的主要语言,由于我的认知限制选择了都知道的一些。是的,JavaScript 属于这部分。这是一个广泛应用的语言,比其他都要广泛。

最终,那些由于“it’s”和“its”而指出我不够认真的家伙们:我非常抱歉。你们是对的,在这个 65kB 的文本中确实有地方打错了。已经修正了,现在我希望你们能够读得通顺了。请直接向我指出类似的错误,因为我从来不用任何拼写检查。

总结

任何将 Go 源代码与其钟爱 C 系语言比较都表达了一个基本的观点:Go 到底哪部分是你喜爱的,还有哪部分是你憎恶的?这多少都会让每个人烦恼,取决于你如何让它变得更好一点。它创造了不同的感觉,因此不能将其与其他一些语言做简单的类比。这很好!

最终,得到了一个简单清晰的容易学习但却不同寻常的类型系统。向计算机科学初学者推销 Go,这可能是主要的障碍。他们通常都被教育使用 OO 的思想。然而,这是简单和安全的,这使得 Go 更好的适应了自学成才的程序员。

如果你有传统的 OO 背景,“Effective Go”是值得读的很棒的文档。有这样的文档很重要,但是这并不足够。研究并编写这个综述相比同时在编写的应用来说,教会了我更多关于 Go 的东西。以前做过的多事情用 Go 来做会有不同,但是会同样好(甚至更好一些),我之前并没有意识到这些。总是有种感觉“Go 不能做 X”,而实际上可以做得很好,只是方法不同。

关于 Go 的潜力比较公平的猜想,留意 Go 的年龄吧。第一个发布版不超过 2 年的时间,并且在今年就已经标识为稳定了。看看 Go 今天已经取得的成绩,设想一下 Go 也有类似 Java 或者 JavaScript 那样的商业背景。对于新的语言,一个成功的好例子是 Java,而对比 Go 跟 Java 1.0 的功能。我们已经有了赢家。

但对于推进这样的潜力,这个语言需要一些动力。包括通过开放的、活跃的、正在增长的社区,或者通过企业的回馈。我更喜欢社区,但是对于实际成功的案例,可能必须有一些企业的参与。来为 Oracle 是否会搞砸 Java 的生意下注吧。^_^

真的,对于当前所有的流行的系统语言的缺陷来说,Go 就是答案。它只是需要证明。

最后要说的是,我已经在互联网上看到了相当数量的对 Go 的批评,这些是不能忽视的,所以得说说:这样做的多数人并没有真正去了解它。Go 是不同的,即便是看起来它就是某种 C。它不是。它不是 C++ 或者 Objective C,也从来没有尝试成为这样的!所以,请不要再说“我们已经有了 C++/Objective C,谁还需要 Go?”这样的话。看看 Go 是如何尝试用完全不同的途径来解决问题的。接受 OO 可以用不同的途径来实现这一现实。你可能选择忽略它,但是如果你使用 JavaScript,你已经在用一些不是基于类的 OO 了。不要只是接受它,却用不同的方式来发挥这种力量。对于其他人,那些认为 Go 还不足够好的:记得这是一个解决实际问题的语言。它就在那。它能工作。用结构优美的语言来解决实际问题,虽然它不稳定,还未完成,并且不够快?对于细节吹毛求疵很容易,但是要做一个真正的产品,需要处理所有约束。这就是 Go 能做到的。

最后的最后:感谢社区。你们反馈了许多有价值的东西。请继续纠正我的错误,偏见和其他任何事情,应该还有。我希望这个文档将帮助那些潜在用户接触 Go。要做到这点,它需要是正确的。请吹毛求疵吧。(译注:对于中文翻译也一样。我囫囵吞枣的翻译,总是错漏百出,悲情……)
———————–翻译分割线———————–
总算填了这个坑,好深的一个坑啊!不过也确实帮助我更加深入的了解了 Go,得谢谢原作者的努力!他写这篇综述,真得很用心。
无论如何,总算翻译完了。有问题,大家直接 comment 反馈吧。

Join the Conversation

10 Comments

  1. 很深的一个坑,遇上Go,才发现“人生苦短,我用golang”的重要性了,她虽然年轻,但是却令我一见钟情,期待Go,相信她一定有更好的未来。

  2. 本来也应该是翻译成“Go怎么优于其它类C语言”的,不过我觉得不太符合作者的文风。作者明显大量用了夸张的手法来表达自己的观点,所以就标题党了一下……或者翻译为“类 C 语言之王”,嗯,嗯,很霸气的感觉。

  3. 第二条引用后的“我完全统一这点。”是“我完全同意这一点。”吧?
    “其中大多数是架设,”里的”架设“是”假设“吗?
    “有一个另我相当好奇的更好的语言设计暗示”,这里的“另”应该是“令”吗?

Leave a comment

Your email address will not be published. Required fields are marked *