程序员的呐喊(A Programmer's Rantings)

在最遥远的国度,比函数式王国更远的地方,有一种传说中的地带叫做lambda the ultimate。相传那个地方根本没有名词,只有动词。虽然那里有东西,但它们皆由动词创造,若传闻不虚,那地方连睡觉前数羊用的数字也不例外,而羊是当地最流行的货币。数字0就是lambda(), 1则是lambda(lambda()), 2则是lambda(lambda(lambda()), 以此类推。在这个传奇国度,每样东西不管是名词动词还是其他什么,都是由lambda这个初始动词开始构建出来的。

在现实生活里的政治软件自由主义者,在保守派眼里就是一帮邋里邋遢,缺乏纪律,天真无邪,没有原则,彻头彻尾的“坏”工程师,而保守主义者在自由派眼里都是偏执狂,只会散播恐惧,自欺欺人的官僚主义者。重申一下,尽管我觉得这两个阵营永远不会达成一致,但是相互理解一下对方的价值观,仍然有助于双方达成妥协。最低限度,这种保守派和自由派的分别也能帮助彼此避开对方。我觉得一支和谐的团队最好是有单一人群组成,要么全是自由派,要么全是保守派,免得双方不停的发生理念上的冲突。这就好像每个地区的驾驶风格都不一样,霸道一点没关系,大家都一样就行了。

自然而然的,我们就有了下面一些结论。

最后一个有趣的地方是很多非常流行的语言,只是温和保守,和那些非常动态的兄弟姐妹比起来,甚至有些还自认为是相当保守的。这里我要再强调,C++, C#还有Java这样的语言,之所以能在市场上取得巨大的成功,完全是因为他们知道怎么左右逢源,这一点和真正的政客是一样的,向往自由的程序员可以把C++当作C来用,而偏爱保守的程序则可以有各种静态类型建模的手段可以用,用到什么程度完全取决于他们需要多少安全感。Java呢?基本上也是半斤八两。同时具备“随心所欲”和“随手关门”两种思想已经被证明是获得市场认同的关键所在。营销也是一样,它的作用是要让自由和保守两大阵营,都觉得它契合自己的理念。

内部调查显示,Google工程师觉得妨碍特性升级和快速发布的主要障碍是官僚主义,周转率高,人事复杂。Google曾多次努力试图削减这种官僚主义,然而他们却一次又一次地被工程师自己给抵制回来了(没想到吧?)。因为这些人已经变成了死硬的保守派,会主动当然更多的是被动抵制,更具灵活性的方案和技术。过去5年里,Google内部的技术转向绝大多数都是保守的。对于我这样的自由派人士来说,目睹这一切实在是太让人扼腕了。好在我为自己找到了一个双方阵营都觉得有价值的位置,在我自己的团队里,还能继续保持自由的风格而不受外界干扰。

身为程序员,不沉迷于性能的话,还叫什么程序员呢?是不是有点讽刺?之所以那么说的部分原因是,远比我们现在用的语言生产力更高的语言是真的存在的。可惜它们的威力大都没办法在我们的硬件上发挥出来,因为这些语言是为理论上的虚拟机而设计的,而这些机器通常又不是(不正式地)由语言本身的能力定义的。假如无法匹配,硬件自然会拖语言的后腿。

大多数Java以外的JVM语言都有这个问题。他们需要硬件(硬件是抽象的概念,任何东西都是硬件)来支持非本地跳转(long-jump)和尾递归(tail-call)优化,可是JVM没有在它的抽象机器定义里支持这些功能。Lisp也是一样,它跑的机器都不是Lisp机,所以压根发挥不出威力来。要是有这样的机器,我刚才跟你打保票,C++在上面跑起来会慢到无以复加。不过可惜程序员关心的不光是性能,他们还很不愿意学习。

这正是另一半讽刺的地方,程序员非常在意性能,他们愿意为此花费无数时间去摆弄算法和数据结构,压榨程序里的每个指令周期和自己,却不愿意用这些时间去学习在新硬件上的新语言。哪怕这门新语言能让他在相同的时间里写出花1000倍的程序,或者只要1/1000的时间就能写出性能相等的程序。

你知道吗?约翰冯诺依曼在生命的最后10年,单枪匹马建立起一套基于细胞自动机的计算理论。你现在用来读我博客的计算机,只不过是它该死的原型机,他原本是打算抛弃它去找一个更好的。可是后来他死于癌症,就像我的兄弟大卫,就像千千万万原本可以活得更久作出更多贡献的人一样。我们在攻克癌症上也没有什么进展,因为我们的电脑和编程语言都是可悲的垃圾。

不过核心问题并不只是Java本身,而是更深层次的东西。我现在越来越觉得(即便是5年之后再来看这篇文章)其实所有的代码从某种程度上来说都不是好东西。代码是负债。所有的问题,简单性,维护性,工具链,分析性,构建等问题,都是由代码造成的,代码量越大问题也就越严重。我知道我们都热爱代码,我喜欢写程序的程度不输于任何人,写程序很好玩也很重要。但是,代码也是数据。这一点大家都知道吧,而且我们也都知道数据量变大会产生问题。就算是(可能是)全世界最擅长处理大数据的Google,也很容易制造出大到无法控制的数据集。这里的道理很浅显,人类能处理的代码量是有限的,这一点无可否认。代码给人的感觉通常很轻柔,但实际上代码属于高度结构化的文本数据,具有异常复杂的处理要求。上至一定规模之后,任何公司都会觉得头疼。

不过现在大家都清醒过来了,不是吗?设计模式不是特性,委托,代理,桥接也都不是。它们只是提供了漂亮的盒子,以松散的方式来装载特性。但是别忘了盒子,盖子和隔板自己也是要占空间的,设计模式也不例外(至少在四人帮的书里所介绍的大多数模式都是这样)更悲剧的是四人帮模式里唯一能精简代码的解释器模式,却被那些恨不得把设计模式纹在身上的程序员忽略了。

依赖注入是另一个新型的Java设计模式,Python Perl JavaScript程序员大家都没听过吧,就算他们听过他们也能(正确的)得出他们更不根本不需要这种玩意儿的结论。依赖注入是一种惊人的描述式架构,能让Java在某些方面和更高级的语言一样,变得更动态一些。你猜的没错,依赖注入会让Java代码变得更大。变大是Java中无法回避的东西,成长是生活的一部分,就像是俄罗斯方块,不过积木和积木之间的空隙都填不满,结果只能越堆越高。

学习数学的正确方法应该是忘记那些算法和证明(绝大多数情况下),去了解那些技术的方方面面:名称,作用,大概是怎么计算的,历史有多悠久,(有时还是可以了解一下)是谁发明的,局限性在哪里,相互之间的关联是什么。这就好像是数学里的素质教育。你问为什么,因为运用数学的第一步就是要界定问题。当一个问题在手却不知道如何下手的时候是最花时间的。但假如你看出这是一个微分问题,或者是凸优化问题,又或是布尔逻辑问题的话,至少你就知道从哪儿着手寻找答案。现在的数学技术和分支科学科的规模已经非常庞大了,如果你连组合数学是什么都不知道的话,那你也不太可能识别那些属于符合数学的问题了,对吧?

TL;DR