《 宏论·注脚》是gigix在7月19日的一篇blog。应该是评论我的《OO丧钟》的系列文章的吧。虽然没有什么直接的评价,除了“越写越没料”、“已然是开始扯淡了”算是一个评价之外。然后呢?然后他就祭出了一个大大的法宝:《SICP》,中文名称是:《计算机程序的构造和解释》。这书赫赫有名,最近o6z也在推荐大家去看:
赶快去学习sicp、
从直觉到自觉的迈进。
SICP这本书我看过没有呢?我的确是很认真、很认真的看过的。不但看过,而且思考过,不但思考过,而且还有了很多收获和自己的结论。否则我的《OO丧钟》系列中,也不会举SICP中提到的例子。gigix的话没有错,他引用的TrustNo1的第一句话更是没错,“这SICP就要年年看、月月看、天天看,每道习题、每个注脚都要认真看。”
那么,gigix错在哪里呢?看SICP不看注,固然会有遗珠之憾。但是如果看注不看书,会错过多少好东西呢?如果仅仅一头扎下去看,不抬起头来思考,这书不还是白看吗?
没办法,gigix既然没有认认真真的看这SICP,也没有仔细看看我的文章,更没有足够深入的思考,我只能更进一步的掰碎那细节里的点点滴滴,慢慢的说给大家听了。
我手头有一本《计算机程序的构造和解释》的中文版,我们来翻到第132页,要真正理解注脚第118号,至少得从这一页的《2.5.2 不同类型数据的组合》这一节开始看起。
在此之前,老师们已经向我们展示了常规数、有理数、复数的各不相干的四则运算定义代码。现在他们给自己提出了新的挑战,原文是:“我们至今还没有考虑的问题是下面事实:定义出能够跨过类型界限的操作也很有意义,比如完成一个复数和一个常规数的加法。”
这话是不能跳过的,不能想当然的认为:是啊,这里正式提出了一个跨类型的需求。问题在于,这个需求为什么有意义?凡是跨类型操作就是有意义的吗?什么样的跨类型操作才是有意义的?一个复数和一个常规数的加法自然是有意义的,一个整数和一个角度的加法有意义吗?
复数和常规数相加这个操作之所以能够有意义,他的前提是:复数和常规数在本质上是“同质”的。这话有点拗口,再表述一遍,只有具有相同本质的数据,操作他们才有意义。
进一步从操作定义的角度来说,add(或者其它函数)操作的只是那个本质。在操作过程中,add只认识一种数据,就是复数。一个复数能够和一个常规数相加,就需要两个前提:
常规数与复数具有相同的本质;
常规数与复数,能够转换成同一种形式的数据表示;——实际情况下,就只需要常规数进行转换了。
前者可以称之为:本质性需求
后者可以称之为:形式化需求
有一篇blog也讨论到这个本质性需求的问题,我一时没有找到,似乎名字是叫《面向对象是一种命名机制》。OO的做法是通过命名加继承语法,来进行本质推导的。canonical_cqh的 我看OO的哲学,也提到了类似的观点。问题在于:这样的推导非常困难,这才引出了SICP的那个第118脚注:
这句话也出现在本书的第一版里,它就像20年前一样正确。开发出一种有用的,具有一般意义上的框架,以描述不同类型对象之间的关系(这在哲学中称为本体论),看来是一件极其困难的工作。10年前的混乱和今天的混乱的主要差异在于,今天已经有了一批各式各样的并不合适的本体论,他们已经被嵌入到数量过多而又先天不足的各种程序设计语言中。举例来说,面向对象语言的大部分复杂性----以及当前各种面向对象语言之间的细微而且使人迷惑的差异---的核心,就是对类型之间通用型操作的处理。我们在第三章关于计算性对象中完全避免了这个问题。熟悉面向对象的程序员将会注意到,第三章的局部状态说了许多东西,但是根本没有提到类,和继承,事实上,我们猜想是,如果没有知识表示和自动推理的工作的帮助,这些问题无法仅仅通过计算机语言的设计的方式合理的进行处理。
我们现在再来看OO的困难,以及我前面提到的两个前提,应该就能够理解我的解决方案的意义了。
在我看来,试图在程序语言中,准确无歧义的,定义不同对象或者数据类型的各自本质与共同的本质,是非常困难,并且注定会失败的。因为OO的努力方向,根本就错了。而在我的DJ语言里,数据类型的定义,根本就不考虑“本质性需求”这样的难题。对于DJ来说,数据能否跨类型操作,仅仅取决于它们的定义是否满足同一组形式化判定条件。我只考虑“形式化需求”!
因此,我才特别强调:“其实所有的数据类型之间,并没有任何关系,他们之间的那个as,只是为了解决check代码的复用需要,任何数据,只要能够通过某一类型的check,那么它就实质上属于那一类型。就这么简单,无歧义。”
我再表达一遍:as不是extends,没有继承父类本质的含义,仅仅是一种代码重用的手段。两个不同的类型的数据,能否跨类型操作,这个问题在我来说,其实是可以分成两个问题的:
1、这两个不同类型的数据,是否能够被某一个操作,判断为同一种类型。如果可以,就没有任何问题,直接操作就是了。
2、如果不能被看成同一种类型(通过datatype check),那么就必须显式的定义dynamic_cast,将其中的一个数据,或者多个数据,转成某一操作所能接受的类型。
在这样的定义之下,程序员不需要去考虑那些乱七八糟的本体论问题,继承结构问题,只需要从数据类型check,与代码重用的角度,考虑问题。这样一来,世界就清静了!
当我们以这样的数据类型形式,来定义那些几何图形的时候,没有任何滞碍之处,我本以为已经能够让大家理解了这datatype背后的根本思想了,谁知道......我还要再说那么多!
再说那第二个脚注:
对象模型对世界的近似在于将其分割为独立的片断,函数式模型则不是沿着对象间的边界去做模块化。当对象间部共享的状态远远大于它所共享的状态时,对象模型就特别好用。这种对象观点失效的一个地方是量子力学,在那里将物体看作独立的粒子就会导致悖论和混乱。将对象观点和函数观点统一起来可能与程序设计关系不大,而是与基本认识论有关。
但是这个脚注,所注的正文gigix怎么没有看到呢?
本章开始时提出了一个目标,那就是构造出一些计算模型,使其结构能够符合我们对于试图去模拟的真实世界的看法。我们可以将这一世界模拟为一集相互分离的、受时间约束的、具有状态的相互交流的对象。或者可以将它模拟为单一的、无时间也无状态的统一体。每种观点都有其强有力的优势,但就其自身而言,又没有一种方式能够完全令人满意。我们还在等待一个大统一的出现。
注意看那些红颜色的字,现在大统一出现了吗?没有!所以,SICP的作者们都还在等待!而TrustNo1却说:“凡是觉得自己在软件设计上有啥洞见的,请先翻阅SICP。如果SICP上写了的,说明你生不逢时;如果SICP没写,基本上可以断定你是错的。”
现在大家可以比较一下,是谁看书不认真呢?