微软架构师谈编程语言发展(三)
译者:程化
Herb:我想,我们有必要在“函数型”编程领域做一个进一步区分,将其划分成两个部分。我非常同意Anders和Erik的意见。我不太同意的是这样的措辞:我们之所以继续使用“命令型”编程语言,是因为这是大家目前所能理解的;通用程序员目前的工作并未取得巨大的成功;市场对于“所有的东西都是表达式,所有的语言都应该是表达式类型的语言”这样的理念已经非常接受了;“函数型”语言是“串行执行”的好药方。我们要想使“函数型”语言运转良好,关键的并不是处理好基本的表达式问题,而是处理好lambda表达式和副作用的问题,关键是能够将表达式作为第一级的编程要素来使用——LINQ也是最近才在做,关键是能够指出lambda表达式和Closure(译者注:函数型编程语言中的一个概念,可以方便地组合函数,返回函数)的副作用。实际上,最后这点目前是缺失的(Anders也附和着:对,对)。这些东西在“命令型”语言中也是要处理的东西。我为什么提这些?因为我觉得说“函数型”语言是方向,目前的“命令型”语言不够好,因此是垃圾,必须要抛在脑后,全面采用“函数型”语言这样的说法不对(译者注:呵呵,对Anders的说法有点急了,毕竟是泡在C++上,对C++有感情的人)。我认为,对于“函数型”语言能够帮助程序员完成哪些工作,目前还不太明了。比如,能够用它写通用代码吗?能够用它系统级代码吗?当然,“函数型”语言有不少我们能够应用的好东西,比如lambda表达式,比如Closure,C#借鉴了,C++也在借鉴,这些语言因此增色不少。关于“函数型”语言还有另一个问题,那就是有两种类型的“函数型”语言,一种是没有副作用的,因此就没有共享的易变的状态的问题;一种是人人都在使用的,对吧(译者注:显然Herb认为“没有副作用”的理想情况是不太可能的)?因为你不太可能说,“瞧,我是完全并发安全的,因为每次我从XX(译者注:听不清)向量中得到一个拷贝,或者我使用XX(译者注:听不清)元素的时候,我都是取得一个拷贝”。确实不错,这里是没有共享的易变的状态,但是是否能够完全并发安全则不一定。
Anders:是的。我的意思是,在类似C#或VB这样的“命令型”编程语言中加入“函数型”结构,能给我们提供“以函数型风格”写库的能力,从而我们就能够非常明确地说,如果你能保证传入的lambda表达式是纯粹的函数,我们就能保证正确地把它分散到若干个线程或者CPU上,最后把它综合起来,给你一个正确的结果,我们能够保证代码运行得更快,同时你还不用作任何编码上的修改。如果你在写一个大大的For循环,我们永远都不可能保证做到前面所说的,此时,“函数型”编程能够提供给你的是一系列表达式,再加上“把代码当作参数传递”,“类型推论和泛型编程可以正确地绑定所有的类型”这些特性,这样你就能更方便地编写“可组合的算法块”。
Charles:这样一来不就削弱了抽象吗(译者注:Charles可能想的是程序员不需要再关心“可组合性”,语言和运行库应该保证这件事,而现在听起来并非如此)?
Herb:呃,我很同意Anders的意见,我想指出的是,当前所有的语言都有意不保证 “没有副作用”。之所以如此的原因是,除非所有的语言都添加一些机制让程序员可以清除副作用,我们这些做语言的人不敢打这个包票。但是,添加这样的机制涉及到众多参加者,大家一起思考、讨论什么是最好的方法的过程会很漫长。我们所做的是相信程序员,因为我们自己不知道。然而,程序员在很多情况下也不知道,因为他写的函数要调用其他的库。这里“可组合性”又浮上水面了,程序员根本不知道他用的库有怎样的副作用。一般说来程序员会再增加一层间接性,但是问题依然存在,没有人能够清楚地知道副作用,除非他拥有涉及到的所有的代码,这就是难题所在。上面这些讨论对“锁”也适用,因为“锁”也是个全局问题,对于“可操作性”是个障碍。
Brian:(译者注:在Herb说话的时候已经很着急地想说了几次)在这点上Haskell做得很好,Haskell是“永远没有副作用”的范例。
Erik:是的,但做到这点的过程也是痛苦的,因为并非所有的情况都一目了然。一旦你的(库)代码有副作用,而且因此使程序员的代码必须按照某种顺序执行(因为副作用的关系,该程序必须先干某事,再干某事),某种意义上你在用汇编语言编写东西,因为程序员将不再能用“表达式+表达式”的方式来写代码,他必须决定先对某个表达式求值,再对另一表达式求值,再把值加起来。因此我认为我们在这点上干得还是不够漂亮。
Brian:现在,我们在“流库”上有例子。好消息是,我们已经有Haskell向你展示如何以“可行性”方面的代价,换来用绝对纯粹的方式来做事。当然,除Haskell外我们有各种“杂牌”语言。呵呵!
(众人均乐)
Charles:这是个供研究的语言吗?
Brian:是的,我们将它设计为供研究用。
Anders:没有纯粹的好或坏,我认为,虽然进展缓慢,我们仍然快到一个令人满意的中间点了。我完全同意说,如果我们确实能够保证函数的纯粹性,生活将会非常美好。最终我们必须要做到。
Brian:在研究领域,大概有20多项工作与此有关——契约语言,契约和限制,等等。
Erik:但是,不少的副作用也并非坏事,如果我的函数使用了一个局部变量,这就是使用了一个状态,但是,函数本身还是纯粹的。如果你想要完全避免副作用,我觉得会非常困难,一些东西可以是局部不纯粹而整体纯粹的。
Herb:回过头,让我们从整体上看看“可组合性”。让我吃惊的一件事是,很多时候,人们甚至都没有意识到这是个问题。他们并没有意识到自己实际上经常碰到这个问题。整个软件工业,整个世界其实已经基于可组合的软件了。在硬件会议上,我经常对硬件公司提到的是(呵呵,通常此时我都是在轰击硬件工业,但是软件业也有同样的问题):硬件的并发问题被仔细地探索过了,而且,当前消除共享易变状态的最好办法就是“锁”;但是,锁是全局的,是一种全局资源,不能被组合;“被锁”是经常发生的事情,而拥有一个锁时,我还能调用任何其他的未知的代码,这就破坏了“可组合性”。说到这里,有的听者往往一脸茫然:这有什么问题吗?我于是指出,好的,你们是否上网下载别人刚刚发布的,自己喜欢的新软件,比如,某个浏览器,3个插件,然后就用呢?大家回答:是啊。于是我再指出,你们是否意识到了,当你们这样做时,经常地,这些软件都是第一次在最终用户的机器上被组合,被使用?既然如此,你们怎么可能对其进行测试?这时,屋子里有百分之十的人会露出恍然的表情,因为此前他们没有想过这个问题:这些软件是第一次在最终用户的机器上被组合,我们怎么进行测试?正因如此,“可组合性”是更加重要的一个问题。更不用说我们现在有AJAX,应用程序,以及众多的其他插件经常被下载,而且被要求在同一个用户界面中协调工作。