技术开发 频道

从“UML何时死掉”谈起

【IT168 技术文章】得了一个机会(1),我问Ivar:“UML什么时候才会死掉呀”。我无意用这个透着促狭味道的问题去为难大师,实在是因为这是我一直以来思考着的问题。向UML之父去求解,自然是最好。

  Ivar细毫没有认为我是在为难他,他诚恳的回答让我在那个会议中陷入了深思。他说:“什么时候面向对象死掉了,UML就死掉了”。(2)

  一个问题看起来很复杂,但它的答案可能非常简单。一个答案看起来非常简单,但它可能是最正确的。一个正确的答案,也许毫无意义,但也许,那就是大师的答案。

  很多我们现在看起来是非常“理所当然”的事情,就曾经困扰着大师们。比如说,我们现在都知道程序的基本逻辑是顺序、分支与循环。那么,“为什么顺序、分支与循环是基本逻辑呢”?“作为基本逻辑,它们充备吗?”谁能回答我?如何回答我?

  这是一个艰深的问题吗?我们知道答案,但即使知道答案,我们也答不出“为什么”。然而,真正的大师们是论证过这个问题的。那个提出“GOTO有害”的大师Edsgar Wybe Dijkstra(戴克斯特拉/迪杰斯特拉)就为此写了篇“札记”。他是怎么论证的呢?他说计算机可以理解的人的思想方式,有三种。分别是枚举、归纳与抽象。而,重要的是,Dijkstra进一步的说明,分支(if)是
计算机实现枚举的方法、循环(for)是实现归纳的方法。当他进一步的解释“抽象”时,他说“在现阶段,我发现很难把抽象的作用说得非常清楚”。

  Dijkstra大概是做好全部的准备,来完成这篇札记。他通过数学方法来证明了“分支如何以及为什么能实现枚举”。也就是说明分支对于“枚举”这种思想方法来说,是否是完备的。Dijkstra在写出了大量的数学推理之后,说“上述的笨重证明,也使我自己感到烦恼!但是,在现在,如果真的希望证明这个程序的正确性,我确实没有更好的办法。”Dijkstra引以为佐证的是,“以前,平面几何里的第一批定理的荒诞证明,也常常使我感到同样的愤怒,因为这些定理所论证的事情几乎和欧几里得公理自身一样的‘明显’”。

  Oh! Dijkstra愤怒的原因,在于原本看起来是如此“显然”、“理所当然”的事情,却需要无比笨重的过程去证明它!如同我们明明知道“1+1=2”,但证明这一点,既无趣又令人愤恨。

  Dijkstra这样的证明过程,奠定了“程序正确性证明”这门学科的基础;它的证明结果,是说明了程序的结构性是有限的,例如顺序、分支与循环。这个有“结构有限”的理论,开创了“结构化编程”这样的一个时代。我在《代码之美》的序中说“我们如今仍然在这个时代之中而不知觉于这本书的深远影响”,是意有所指的。因为所谓的“面向对象程序设计”,其根基就是“结构化程序设计 + 在结构上更高层次的抽象”。而这“更高层次的抽象”,就是“对象”。

  Dijkstra在1970年前后就完成了这篇札记,而我们接下来这40年的时间,仍未逃离大师最初的思想。然而,对这所有的一切,大师最初创见性的想法可以归结于一句:“可用来理解一个程序的种种思维方法之中,我提及以下三种:枚举法、数学归纳法、抽象。”

  为什么是这三种?是不是只有这三种?能不能有更多种?大师没有解释,他只是“提及以下三种”而已。Dijkstra一方面给我们留下了空间,一方面,他足够完备的“论证”说明了基础逻辑必须至少具备“顺序、分支、循环”。而我们,40年来,无有突破。

  Ivar把UML之死,归于一种抽象的失败,或其被更高的抽象所替代。实在是无比正确的,因为UML也是建立在结构化、抽象这样一些基元的理论之上。Dijkstra的那篇札记,被收入《结构程序设计》一书,书里的另外两篇,一篇是“层次结构设计”,讨论的是面向对象程序设计;另一篇是“数据结构札记”,讨论的是数据基本抽象。三部文章,三位图灵奖得主,一个跨越40年的时代,以及程序设计语言分类中的1/2(命令式语言)都承受着这种影响。

  然而,Dijkstra仍然无法论证,或未曾说明过“三种基础逻辑的唯一性”,他只是想当然地说“我认为”而已。

  同样只是从“我认为”开始的,还有图灵机。如果有一个毛头小子跳出来说,“我认为”计算机应该象一个大笨象吃意大利面条;大笨象要有肚子,面条上要打孔。Oh,很好,这个毛头小子立即会被哄出“计算机科学”的神圣殿堂。问题是,图灵就是这样一个毛头小子。于是他的这一构想就被称为“天才式的创想”。

  然而,真的只是这样吗?

  算盘用了几千年,谁问过“算盘为什么能算东西”?算珠、进位、栏,这些东西,是不是基本的存储结构?用算盘的“我们”,是不是计算单元?珠算表是不是运算规则?那些珠子表达出来的“0~9”的排列,是不是输入输出的界面?

  “我们+算盘”就是一个完整的计算系统。这个计算系统的完整性,是图灵用了一个假想来说明的。图灵不过是用一个假想描述了一个事实,而这个事实,“看起来”能被机器实现。于是,我们的计算机时代就开始了。

  图灵是否证明过“大笨象吃意大利面条为什么是一个完备的计算机系统”呢?不,最初等的问题,往往难于证明。往往,他的证明过程,或应用过程,只是触发了一个想象。

  对计算机根本问题的思考,许多会追溯到哲学思想的层面。IOPD和PDIO的问题,程序=算法+结构的问题等等,就是这一类。还有一些会追溯到人类行为学、语言学等层面,例如语言、语法、语义,以及有没有语用这样的问题。大多数时候,真正推动了计算机发展的,不是对具体问题的推理求解,而是对问题本身的抽象。在Dijkstra的叙述中,抽象更象是终极武器。按照Brooks的观点,“数据的表现形式(数据结构,抽象的结果之一)是编程的根本”;按照Dijkstra的引述,“引用未解释过的名词阐述公理或定理和作用于未解析过的操作数的(命了名的)运算两者之间有着某种平行的相似性”。

  无论如何,我们“做一个计算机”,原始的目的不外两个。其一是“让它计算数学”,其二是“让它像人一样思考”。注意,我的确是说“计算数学”,数学是人类的理论学科,“怎么算”,以及算的内容等等,都是我们自己设定的。而计算机的能力,只是计算“数学”这个它未知的对象而已。事实上,我们现在在讲的“命令式”,以及“函数式”,或“说明式”,就只是我们为计算机设定的“最基础的运算方式”。在这个“运算系统”中,“数学”并不是最初设定的。

  命令式如何计算,函数式如何计算……类似于此的问题了解清楚了,我们对这类语言也就了解了。再谈什么高阶函数(higher-order function)、克里化(Currying)、延续(Continuation),或发生-迭代器(Generator-Iterator)之类,那已经是具体语言的表象,而非“这一类语言”的本质了。举例来说,JavaScript 1.5还没有实现过“生成器对象(Generator Object)”,但并没有人否认它是函数式语言。反过来说,Generator Object原本就不是函数式语言的必备要素。

  LISP表达了函数式语言的全部“必备要素”,然而LISP七个原子运算也是针对于“LIST”这个结构抽象来说的。对于一个“(顺序的)表”,这七个原子运算是必须的,而对于另一个“(关系的)表”就未必如此了。所以,那这些原子运算,也不必放在函数式的必备要素中。象LUA这样的函数式语言实现方法的出现,也证明了这一点(3)。

  函数式还剩什么?

  真正理解函数式的秘密,是要一个语言一个语言的学习下去么?是要一种运算法一种运算法地学习下去么?我们听完人家说“持续”,于是就开始了解持续,而不去问持续为什么出现在函数式里面?亦或是不是函数式的必备要素?还是函数式运算系统的自身的“问题”?我们正是迷失于种种语言和概念的表象,而最终没能象大师一样去思考“计算机不过是大笨象吃意大利面条”这样的抽象层面的问题。

  我们要改变的是思想,我们要增强的是能力。大多数人只是增强能力,而不改变思想。这就是“我们”——大多数人不是大师的原因。

  感谢Ivar。并不仅仅是因为他给出了一个问题的答案,以及他的谦谨和微笑,还感谢他告诉我们:答案并不是表面上的正误,真正的答案是答案背后的思想。

 

0
相关文章