3 Tribes of Programming - 3 大编程阵营

有一个老笑话,计算机科学其实是一个谎言,因为它不怎么跟计算机相关,也不是真正的科学。

有趣的笑话,每个人都笑了, 然后有人说 “但是它还是跟计算机有些相关性的,不是吗?”。

在外面,有争论是这样的:

“ I’ll happily renounce “programmer” in favor of “applied mathematician” or something, whatever it takes to avoid C. - https://t.co/DsIEo5x4uI / Twitter 有些人认为,我们计算机中的整个现代堆栈(内核、操作系统、浏览器、虚拟机)是用C + ASM 编写的。所以你应该了解 C 和 ASM。

这真的很重要吗?问题的关键在于:一个程序,指令序列与逻辑思想的表达,两者谁更重要?

也可能两者都不是,而程序只是我们为其他人制作的东西。如果没有阅读它的读者,任何一条信息都是无意义的。如果不参考它们所处的外部世界,程序是否也毫无意义?

一个朋友曾经向我吹嘘,他可以用Ada证明大多数程序是正确的,而且完全没有错误。我问他能不能证明这个函数是正确的。

fn sub(a, b) { return a + b }

他说:“当然,这太简单了”。于是我问他他的测试工具如何发现函数有错误的名字,他笑着慌了手脚。

三大编程阵营

显然,程序拥有所有以上这些属性。但我认为从根本上说,程序员有三种类型,按我们最推崇的理想来划分。

我认为每个群体都有自己的准则,和自己的子版块。他们有自己的编程语言、英雄与反派。

编程即数学的应用

第一个阵营认为,编程从根本上说是一种思想的表达 - 一种我们可以用生命来馈赠的数学诗歌。在冯·诺依曼机器上执行它们只是一个实现细节。

带着这种心态,下面这些细节就很重要:

源代码:源代码应该读起来像诗歌一样 - 稠密又浓重,只需要很少的几行代码来表达一个想法。一旦理解,简练的程序看起来就像是对你的程序的美丽而明显的描述。 源代码的精炼比执行的简单或快速更重要。高级语言比低级语言更优秀,因为高级语言能让你更清楚地表达你的意图。 执行:程序如何被计算机执行只是编译器的一个执行细节,代码的简单比执行的快速更重要。 正确性:如果准确的实现了规范,那么程序将会是正确的。我们可以使用Ada来证明程序的正确性。 UI:代码如何与人进行互动与他程序的实现是两码事,漂亮的代码比漂亮的用户界面更重要。 举例: Rich Hickey, Brett Victor

这类程序员可能是最不常见的,这可能是因为这种人很难找到工作。在 stackoverflow 统计的的所有语言中,Haskell 在的周末使用率最高。

现代编程语言的大部分(可以说是全部)的进步都源自于这个阵营的努力。如果你用 React 做了一个网站,你应该知道,不变性模型和把你的观点表达为从数据到 DOM 的纯函数模型来自函数式编程。实际上,大多数现代语言的功能都是由那些把编程当作思想的人发明的。几年(或几十年)之后,这些功能被复制到更流行的语言中,并被当作新的想法。

我有一个朋友,他花了几个月的时间喜欢上了 J,他最终用 J 写了一个小游戏,他把他的代码描述成这个完美无瑕的水晶。后来他想把它做成多人游戏 - 但要做到这一点,他就必须处理迟延问题。而这将需要分割一些美丽的内部流程。他无法忍受这一点,所以他完全放弃了这个项目。

这个故事很有趣,但我有点嫉妒我的朋友。我打赌他学到了很多东西,而且玩得很开心。这样的经历使我们变得更加优秀。

去年年底我参加了一个 Haskell 的短期课程,我向主讲老师提出了挑战。我告诉他:“这的确不错,但我打赌我仍然可以用我的实用语言比你更快地做出有用的软件”。他说不可能 - 使用 haskell,他相信他可以实现任何我可以实现的东西,而且更快、更好,代码更少。我们没有验证这个说法 - 但我仍然想知道 - 他是对的吗?

最喜欢的语言:Haskell, Lisp, ML (Ocaml, etc), Closure, ADA

居所:FP meetups, Lambda the ultimate, Strange Loop, Research.

Steve Yegge making fun of this tribe

编程即硬件 Hack

第二个阵营认为,编程从根本上是与计算机的硬件联系在一起的。没有计算机就无法运行任何程序,因此要进行有效地编程,我们必须时刻牢记计算机 - 硬件和软件。

优雅和美丽不仅来自于简单的代码库,而且来自于该代码库以一种优雅和有效的方式驱动硬件。

因此,优雅之处在于:

源代码:代码应该是简洁的,但是简介的代码不如干净利落的执行来的重要。低级语言通常优于高级语言,因为你可以更明确地知道计算机在执行你的代码时在做些什么(因此,你有更多的空间来进行优化)。 执行:计算机如何执行你的代码是最重要的。不考虑执行的编程只是在为缓慢的性能添砖加瓦。 正确性:如果一个程序在正常参数下以你期望的方式运行,那么它就是正确的。执行的优雅性比正确性更重要。如果一个理论上的问题由于机器的工作方式而无法在实践中发生,那么它就不是一个真正的错误。一个程序必须有快速的执行速度才能被认为是正确的。 UI:代码如何与人互动是与它的实现应该分开考虑。让硬件的限制来指导用户体验是可以的。 举例: Poul-Henning Kamp, Michael Steil, The 8-Bit guy

这里的关键是要考虑到计算机和你的运行程序的整体性。按照此方阵营的说法,编写好软件的最佳(唯一)方法是全面考虑它将如何运行,以及我们的程序将如何与其他硬件和软件互动。做好这一点,就能达到硬件的共鸣,一切都像上好油的时钟一样运行。这种快乐就像驾驶一辆你能听到和理解的手波车一样。

任何混淆计算机如何执行你的程序的行为对执行方来说都是危险的 - 因为它增加了更多需要考虑的变量。因此,这个阵营的人经常嘲笑垃圾回收器,或者 JS 性能基准测试结果的大闹改变了我们编写代码的方式。C 语言编译器中的未定义行为是一个持续争论的焦点。

在现代应用开发中,我们的计算机速度已经足够快了,这种思维已经不那么重要。几十年前,你需要对计算机的工作原理有深刻的理解才能编写软件。但现在基本上你使用的任何语言都足够快了,所以为什么要费心学习 C 语言呢?我认识的大多数网络开发者都不懂 C 语言,也没有兴趣去学习指针或手动内存管理。

但是这类工作在很多领域仍然有很大的价值。游戏开发社区仍然用 C++ 编写大部分代码(尽管unity正在慢慢改变这种状况)。安全工程师需要一个系统的理解来寻找漏洞。嵌入式系统工程师不能浪费周期和内存,一旦后端系统变得足够大,性能又开始重要起来。

而且,即使它不实用,但被迫思考机器的问题也是非常有趣的! 例如,PICO-8 施加了专横的 “硬件 “限制,迫使你在设计游戏的时候变得聪明起来。

在这个社区中,我们的计算机几乎所有的性能改进都归功于此,超出了客户的要求。没有人像那些整天考虑硬件的人那样关心性能。但是,如果你把你的电脑当成一台机器,还有什么比无意义的工作更让你感到丑陋的呢?

我真的很好奇 Rust 是否会在这个社区中大火。Rust 本质上是一种由上述第一阵营的语言书呆子设计的语言,为那些关心运行时效率的人服务。他们会接受它吗?未来的游戏引擎会被移植到 Rust 上吗?

与第一个阵营的冲突:

最喜欢的语言:C, C++, Assembly

居所:Hackerspaces, Game dev shops, database companies, CCC, Defcon.

Brett Victor making fun of this tribe.

编程即创造的工具

最后一组人把编程看作是达到美好目的的手段,而不是构建美好的东西。 这个阵营的人描述自己的方式从根本上说是务实的。他们写软件是因为软件对其他人有用。

根据我的经验,这个阵营的人在社区方面更出色。他们似乎更积极,更鼓励新成员,并愿意提供帮助。 我想这是因为你可以通过简单的观察自己来判断你在其他两个阵营中是否做得很好。如果你为其他人制作软件,满足感来自于让你周围的人感到快乐。

我不禁觉得这个地方有点儿没有灵魂。走到极端,这种世界观并不重视工程本身的美。尽管你可能会对其他团体提出相反的批评 - 他们不重视他们的软件如何影响世界。

在这个阵营和我谈到的其他两个阵营之间存在着很多冲突。而且这可能会变得有点刻薄。 我知道很多做产品的人对他们缺乏传统数据结构和算法的知识感到自责。他们觉得被 “真正的” 程序员批判,因为他们不能实现晦涩的算法和二进制框架协议。 在这个阵营的人看来,反正其他人已经实现了所有这些东西。所以,谁在乎呢?

这还算公平,但它也是事实,很多问题是由前端工程师缺乏技术能力造成的。这主要是自我纠正 - 如果你的程序太慢,你知道它并可以修复它。 但是安全工程是真正的危险。如果你不知道如何保护你编写的软件不受黑客攻击,那么它可能就很危险。 而且,即使你被黑了,你也可能不知道这是个问题。

“@jdan Well, then you’re not a very good programmer. Sorry but that’s how it is.” ~ Jonathan Blow (@Jonathan_Blow) June 12, 2015

就上面的言论而言,Jonathan Blow(著名的独立游戏开发者)说,如果你不能逆转二叉树,你就不是一个好的开发者,即使你每天都在写有用的软件。

他说的对吗?这取决于 “好的开发者” 是什么意思,而这又取决于你关心的是哪个阵营。 我认为 Blow 是在第二阵营,评判标准是你知道多少。 @jdan在第三阵营,所以他的评判标准是他做了什么。Jonathan Blow 当然会写出有用的软件,但他的上一个游戏(The Witness)花了这么长时间,原因之一是他自己写的引擎,而不是使用现成的东西。当被问及这个问题时

“ 我对Unity不是很了解。然而,很明显,如果不重写大部分Unity(或添加很多没有的东西,拒绝使用Unity提供的大部分东西),就无法在Unity中构建 The Witness。而我们反正已经有了一个简单的图形引擎。因此,在建立我们自己的系统时,我们可以确保它们确实是游戏所需要的最好的东西。 ”

我怀疑他对第一部分的看法是错误的。但我自己主要是在第二阵营,所以我理解无论如何要写自己的引擎这种想法。我可能也会做同样的事情。

最喜欢的语言:无论做什么都完成了工作。JS, Ruby, Python, Swift, C#.

居所:Twitter, SydJS, StackOverflow, 你附近的公司!

Gary Bernhardt making fun of this camp.

一场安静的战争

我认为我们社区中的很多冲突和分歧都可以用这些术语来表达。并且还有很多程序员之间的误解。

例如,当一个整数溢出时,你的编程语言应该怎么做?如果你认为编程就像数学诗一样,首先它应该给你一个数学上正确的结果。

Haskell (第一阵营):

λ: 23^23
20880467999847912034355032910567 :: Num a => a

Vs C (第二阵营):

printf("%llu\n", 1 << 100); // overflows. Prints 0.

而如果你只是想发布一个产品,你就不必须在乎了。在javascript中(第三阵营),根本就没有整数类型。JS只用浮点数来表示一切。如果它们溢出了,那就倒霉了。

Rust 试图在前两个阵营中各占一席之地 - 成为一门由编程语言书呆子创造的语言,但却能编译出高效的代码。不出所料,这个问题在 Rust 社区引起了长时间的争论。最后的解决方案是这样的:默认情况下,溢出导致的异常在调试模式下被抛出,但在生产模式下安静地工作。

Rob Pike( Go 的作者)对他的语言想要吸引哪个阵营感到困惑。他在 Go 发布几年后写了这篇文章。

“ 几周前有人问我,“你在推出 Go 时遇到的最大惊喜是什么?” 我立刻就知道了答案。尽管我们期望 C++ 程序员将 Go 视为一种替代方案,但大多数Go程序员来自 Python 和Ruby 等语言。很少有来自 C++ 的人。 ”

为什么?好吧,C++ 程序员在很大程度上属于上述第二阵营。他们想知道他们的代码将如何运行。但是 Go 有一个垃圾收集器和一个快速编译器。真的,Go 关心的是如何摆脱你的束缚,让你可以直接做东西。它是为最后一个阵营的人设计的语言,他们想制造产品。那些关心这个问题的人目前使用什么语言呢?Python、Ruby 和 Javascript。所以 Go 的目标当然是他们。

闭幕

下一次,当你看到关于 Javascript 是我们行业的毒瘤还是福音的争论时,或者当你看到像我这样的人对现代应用程序是垃圾感到愤怒时,请问你自己是为哪个阵营说话。 他们是在拥护美丽的代码吗?性能和 “深刻理解”?还是他们只想完成工作和发布产品?

归根结底,代码就是代码。即使我们有不同的理由来写软件,我们所写的东西(通常)是兼容的。即使不是这样(Haskell)- 也总是有很多我们可以学习和窃取的想法。

毕竟,我们都欠对方很多。没有语言书呆子,我们仍然在写汇编。没有系统程序员,我们就不会有操作系统,而 haskell 和 javascript 也会慢得不能再慢。如果没有产品工程师,其他人就会被迫写 CSS。相信我,没有人愿意这样。

格雷斯-霍珀海军少将(Rear Admiral Grace Hopper)成功地将机器理解和产品思维联系起来,并在此过程中发明了一种与机器无关的计算机语言的概念。如果不能同时考虑到计算机能做什么和我们希望计算机做什么,这就不可能实现。

但我个人认为,我们应该渴望成为 Alan Kay 那样的人,同时做到这三点。他和他的团队经常跨越多个阵营的界限。举个例子,他从观察儿童学习 Squeak 和 Logo 中发明了面向对象的编程。他认为有一些方法可以让吃到蛋糕 - 使用现代技术来设计更简单的系统,使其更快、更优雅、对人类更有用。如果你还没有这样做,你应该看看他的每一次演讲。慢慢地看吧。

这当然是我的目标。希望我在70岁以后还能让人们大吃一惊。

This is a version from josephg.