某午夜, 灵感泉涌, 找灯开关, 找笔, 找纸, 找合适的词, 试图再现思维的火花. 提笔, 乱七八糟的中文, 潦草的数学符号、记号, 几行伪代码, 夹杂英文单词. 我常常想, 假如我从来不曾学过数学, 从来不会英语, 我记下的思维状态, 是否会和现在一样; 我在思考的时候, 会不会自觉的使用数学语言, 会不会用英语? 咿呀学语的婴孩, 不会使用人类语言的聋哑人, 又该如何记下他们的思维? 语言符号到底是构建了人类的思维, 还是限制了思维的表达?
谱曲要入五音, 写词要从平仄; 语言要有语法, 文章要有结构; 这些, 包括编程, 计算, 证明, 都是在使用思维而赋之于符号规则系统(语言). 符号系统有其规则和用法. 守其规则, 才能有交流. 如果放弃了语言的交流功能, 则可以解放思维, 自由表达. 如果我们观察乡村里的”土哑巴”(不会正规的手语,手语是自创), 则只有周围的少数人能理解其含义. 从他的思维层到自创的手语层也许是透明的一一对应的, 无碍的. 但出了他的手语层而要求和他人交流时, 其自创的手语又成了交流的障碍. 因此, 规则是用以交流用的语言必须内涵的. “即使狮子会说话,我们也不懂得它”.
语言能力不是与生俱来的. 即使完全通过逻辑, 从原始的事实构建到高层的抽象, 也需要时间. 以数学为例, 从最简单的集合论和代数系统到费马大定理, 完全通透的走过也需要比较长的时间. 而构建这个大厦的砖块只是几条公理, 建造的方法又只有逻辑一种. Wittgenstein (维特根斯坦) 在逻辑哲学论中讲语言是思维逻辑结构的几何投影. 思维逻辑结构建立后, 可以借助于语言, 通过一个人传递到另一个人. 如果语言的几何投影能够被简单还原, 思维就通过语言完成了一次传递. 在A(创造者)脑中, 语言是被构建的. 在B(学习者) 脑中, 语言是被解构并还原为事实的逻辑图像(思想)的. 在学习者那里, 思维是要需要借助语言进行的.
一个人认识到的世界不是完备的, 因此使用的语言集合也只是一个子集. 对语言的理解程度决定于对世界的构建. 仍以数学为例, 研究微分方程的数学家与研究代数数论的数学家如果遇到了, 可能彼此都不懂对方在讲什么. 微分方程世界的语言, 对于代数数论世界的数学家, 是难以解构的, 因为他的世界里缺少语言反向解构的对应物. 回到自然语言, 英语对于很多中国人, 是无法解构的, 因为我们无法分辨这个语素到底对应了什么. 但是假使我有一本字典, 这个问题也就解决了. 抽象的层次, 表达的符号, 描述的世界等所有的一切不同, 都让语言这个逻辑图像呈现出了不同的样子, 而让不熟悉这个语言系统, 或者缺乏逻辑世界对应物的人, 云里雾里.
我们来研究一个设计精巧而简单的语言: 程序设计语言. 程序设计语言是也是表达思维的工具.
程序设计语言有语法, 有规则. 语法和规则是为了让底层的语言理解机制消歧义, 获得一致性的解释. 符号在程序设计语言中发挥了原子的作用. 几乎所有的编程语言都是基于封闭世界假设, 也就是说, 没有声明(被计算机感知)的原子符号, 不能使用, 每个事物都有其本体论含义. 编程语言把真实世界的逻辑关系映射到了计算机世界. 因此受制于计算机世界. 在语言和思维关系当中, 语言的限制造成思维表达能力相对受制, 而相反的, 交流能力却大大提高 (居然能和一台机器打交道了).
图灵完全性从一定程度上解决了语言能力的问题. 一般来说, 编程语言被计算机解构后, 描述计算机世界的能力是相同的. 但是这不代表编程语言的表达真实世界的能力是一样的. 事实上, 不同的编程语言的描述世界的能力是不一样的.
程序设计语言是真实世界的逻辑图像, 这个图像对应了一种逻辑(算法), 一个设计哲学(模式, API)或者一个世界模型(框架). 而这个图像可能位于世界的不同层次. 以Java, C 和 SQL 为例. Java 是对象世界的逻辑图像. C 是过程世界的逻辑抽象. SQL 是关系(数据库)世界的逻辑抽象. 因为世界难以”一言以蔽之”, 因此不同的语言出现是必然的. 三个语言在解题能力上是等价的, 然而, 之所以没有人用 Java 用做关系数据库的语言, 是因为 Java 的逻辑抽象, 不是关系数据库的一个直接几何投影. 因此, 用 Java 这个逻辑图像去重现施加于关系数据库世界的逻辑, 是不够直接简明的, 至少说, 不是最贴切的.
从程序设计语言的使用上, 我们可以清楚的看到, 语言的错误选择限制了思维. 语言的错误选择不在于语言本身, 而在于语言和表达世界的不契合. 以 Java 编程为例, 一开始的 public static void main 封装在一个 public class 中, 不知道吓退了多少从来没有接触过面向对象的语言学习者. 推而广之, 即使一个框架设计再好, 不了解其设计哲学, 仍然觉得难学难用. 不契合来自于四方面, 1. 对世界模型的不熟稔, 造成语言无法使用. 比如, 对MVC的不熟悉造成Struts难学难用. 对IR不熟悉造成 Lucene 难学难用. 2. 太复杂的抽象造成语言难以入门, 比如 Java 模型造成一行Hello, world 都要至少构造一个 class, 一个 main 和一个 system.out (在Java 6.0中已经无须这么复杂). 一个简单的逻辑映射出不属于这个世界的很多抽象. 不必要的复杂性吓退了初学者. 3. 太高层的抽象造成语言难以理解, 比如 LISP, 抽象于 lambda 演算, 至今仍有不少人对于 Y-Combinator 的高层抽象表现出费解; 而对于精通函数式语言的人来说, Google 的 MapReduce 又显得那么自然. 不了解高度抽象的世界模型, 必然是如同看到满屏幕的乱码一样, 无法下手. 4. 太低的抽象造成语言难以使用, 如C, 在没有library 情况下, 链表都要手工构造. 即使想描述一个简单的思维, 都需要映射出很多不必要的底层抽象. 这样让语言的使用者主观上不倾向于用这套语言体系.
[我最近在学习法语, 其名词的性限制了我的表达, 继而限制了沟通. 因为假如我用错了, 别人会不懂. 因此触发了语言和思维的思考. 同时这几天, 我在考虑到底选择什么样的程序设计语言才是好的选择, 因此有抽象层次和语言表达能力的思考. 正巧有一本维特根斯坦的哲学逻辑在手边, 偶翻一下, 触发了思考, 故写下这篇随笔. 在语言选择方面, 我推荐 Python, 我认为它达到了抽象层次和表达能力的契合, 无论你是初学者, 还是高手].