【译文】承认吧,函数式编程可能是笨拙的

2012-07-12 23:00

【译文】承认吧,函数式编程可能是笨拙的

by

at 2012-07-12 15:00:00

original http://www.soimort.org/tech-blog/2012/07/12/functional-programming.html

Original Article: Admitting that Functional Programming Can Be Awkward by James Hague
(Chinese Translation by Mort Yao)


我起初对函数式编程产生兴趣的原因是,它看上去是如此地有悖于常识。

想当年,我还是一个典型的自学成才式程序员,已经学会了BASIC、之后是6502汇编语言,用来实现我自己的游戏设计。我拿起了1985年8月号的那期《字节》杂志,阅读关于那时相当新潮的Amiga个人电脑。那期杂志恰好也是关于声明式语言的一期,刊登了Backus那篇著名的图灵奖演说的全文,以及一篇Hope语言的教程,同其他文章一起。

对于一个Atari 800游戏程序员来说,读这样的材料确确实实相当让人抓狂。我理解了其中的仅仅一部分,完全略过了其他的大块内容,但一个关键的要点激发了我的遐想:不使用可修改的变量而进行编程。那怎么可能做到?我无法在不向内存中存储任何值的情况下写出哪怕最简单的游戏。正是这种看似的“不可能”吸引了我,正如我当初听说汇编语言太难以至于大部分人无法掌握的时候一样。不过,鉴于我已经领略到了杂志上无数一行行用汇编语言写成的游戏代码、并从中学会了编写我自己的游戏,函数式编程却并没有类似这样的直接应用实例。它让我感到惊讶,但我并没有立即去使用它。

多年之后,当我第一次学习Haskell、Standard ML、最后是Erlang的教程时,我终于了解到了不修改变量的编程方式是怎样运作的。从小的方面讲,这很简单。在1985年尚且显得怪异的东西如今已经变得稀松平常了:垃圾收集;使用复杂数据结构而不用担心内存分配;比C或Pascal拥有更少bookkeeping code的语言;但所谓的“非破坏性更新”——至今仍然——显得有点棘手。

一件毋庸置疑的事实是,世界上有数以万计的视频游戏使用了命令式编程风格开发,也许只有那么一小撮——甚至可能是屈指可数的——游戏是使用纯粹的函数式风格编写的。当然,是有用Lisp编写的游戏,还有一些是OCaml语言爱好者的得意之作,但它们从未是以函数式风格写成的。在这些语言中,你可以相当轻松地写出命令式的代码。这么做的理由很简单:如何用函数式风格编写各类复杂的应用程序,并不是那么一目了然的一件事。

通常,我能够解决数据对象之间的依赖关系,而且常常能够找到函数式方法背后内在的简朴性。但是对于其他情况下的应用……嗯,它们将成为难解之谜。用C语言可以稍带混乱但不失高效地解决的地方,纯粹的函数式解决方案要么根本找不到、要么尚需要不少精力方能理清。在这种时候,我感到我像是在和整个系统进行艰苦卓绝的斗争,于是我意识到了为什么它是一条更少被人所选择涉足的道路。不相信是吗?依然认为函数式的纯粹永远是解决问题的不二法门?请看以下简单的例子。

在这之前,我曾经写过一个马马虎虎算是成功的Mac游戏,叫做Bumbler。在本质上,它与那种常见的标准类型的sprite游戏并没有什么不同:众多独立的物件,执行着一些控制行为的代码并且能够互相之间进行交互。这一类代码用纯函数式方法写起来很简单。一只蚂蚁,用一个坐标表示,在屏幕上以直线前进,撞上了屏幕边缘就被删除。这很容易看作一个函数。一小块数据被输入,输出则是另一些数据。

但是行为和互动可能远比这要复杂得多。可能会有一只昆虫去追逐别的昆虫,所以你必须向它传递一个当前存在实体的列表。可能会有一只昆虫能够影响到其他昆虫的繁殖率,当然,你不能直接去修改繁殖率的数值,所以你必须设法返回这类数据。可能会有一只昆虫含住卵就能把它们变成别的什么东西,所以现在需要有一个行为函数能够操纵实体的列表并且做出修改,但你却不被允许去那么做。可能会有一只昆虫能够改变物理环境(也就是,游戏的背景)并且繁殖更多的昆虫。以上的每一种情形实际上都比听起来还要复杂,因为有如此多的计数器、阈值和限制值需要进行管理,加上各种事件发生时的音效播放,整个数据流程无论如何也不能说是一目了然的。

有趣的是,用C语言来实现这些不过是小事一桩。只需要一些递增,一些条件判断,直接调用播放音效的例程和繁殖昆虫的函数,从全局计数器和状态变量的池中进行读写。如果就纯粹的函数式方法来说,我相信这一数据流程可能是令人费解的……如果假设一切事物都完美地在计划中进行、所有的行为都提前有所定义的话。更棘手的是,如果你接过一个单纯的运动函数,说:“好,我现在想做的是,当这个物件从屏幕边缘反弹三次以后,让它对其他的物件施加引力影响。”是的,(用函数式方法)可以做到。想要像C语言一样实现得直截了当?没门。

这里是一种选择:承认函数式编程对于某些问题来说是个错误的范式。没错。我可以打这个赌。但也许是因为几乎从没有人思考过这方面的问题,函数式编程正在吸引着纯粹主义者和那些固执地沉迷于其中的学生。在上面这个游戏的例子当中,有些问题是可以解决的,它们只是需要借助不同的途径。另外一些问题,我不知道如何解决,或者说至少没有找到如同循序渐进的C代码一样直截了当的解决方案。总之你看着办吧……反正我是承认了,函数式编程在某些情况下是笨拙的。在其他时候,它依然极其有用。