1 序
另见:http://www.yanying.wang/SOICOL/
1.1 灵感来源
纵观人类历史,很多有意义的成果往往都不是靠严谨的逻辑推理得来的,而是一时的灵感迸发,先有了结论,然后再去寻找现象去证明结论。
所以有些事情,你不用管他对错有无,感觉对了就要开干。
我大概在2015年的时候,开始使用Emacs编辑器。早之一两年学习Ruby语言的时候我就已经知道了LISP语言。大概从2018年开始,我才终于得以开始投入时间到Racket语言的学习上。
也是在2018年的时候,有次出差北京在和同事们的一次交谈中,我谈到说,自己曾经在维基百科上面看到过一些提及,说在一些学术研究的边缘领域中,有讨论用梵语来替代英语作为编程语言的宿主语言,原因是英语的词法规则不工整(动词时态变形,名词主谓宾等等的单词变形规则不够严谨),梵语是已知的最为严谨的语言,从语法词态构词规则等等的角度讲。
作为天生的中文使用者,但近代科学起源于西方,一二三次工业革命均发生在西方,编程语言亦起源于英语国家,这导致大量的一手文献资料都是英语写成的,工作和学习中不得不接触英文,这常常让我不禁的去思考中英文的语言差异。
我想或许,梵语之说显然是西方人局限于西方语言(德语、法语、西班牙语、意大利语都算是拉丁语的方言吧)的固有模式所做出的推演。而实际上,中文才是更值得尝试的宿主语言,特别是对LISP来说。
2021年夏,我的这个想法变得越发成熟。2022年夏,我着手了实现。
另可见我曾写过的文章:《由汉语编程所想到的》和《我的一种关于lisp方言式的汉语编程语言的构思》。
1.2 LISP的基本语法规则
它首先由括号约定执行优先级;
然后由每个括号中的第一个字词表示此括号内容的含义(称为前缀表达式)。
要理解S表达式,好有一个比喻:自然语言说的“我 爱 你”(主谓宾结构),如果采用S表达式说它的话是“(爱 我 你)”(谓主宾结构)。
1.3 LISP代码为何难以读懂
首先,“谓主宾”的排序方式是不符合大家所使用的的自然语言常见的“主谓宾”排序的,这造成了一部分的阅读障碍。
另外,就是因为括号所衍生出来的语法了:
因为S表达式是前缀表达,相当于把“谓词”前置了。但此时的“谓词”却仅仅只是一个简单的谓词,没有清楚明确的表明出“谓词随后的数据”和“谓词”之间的具体关系(谓词之后的数据将怎样被处理,数据有多少,数据都是什么类型的等等)。而这种处理关系也并非是单纯而统一的,而是根据这个“谓词”的不同而变化的(因其可以被定义和扩展)。而这种变化在无形当中会造成思维上的负担和混乱。
比如(+ 1 2 3)表达式的“谓词”是+,整个表达式的意思是"1加2再加3"。
同样(if 1 2 3)表达式的“谓词”是if,但其意思却是"如果1是真,则返回2,否则返回3"。
1.4 如何改进LISP的难读性
LISP诞生在上世纪五十年代,现今的编程语言相较之,舍弃了S表达式,引入了更多的关键字来替换括号,表达式也大都采用符合自然语言使用习惯的中缀表达。
但LISP的精髓之处就是他的S表达式,舍弃S表达式并不是我要追求的。
所以进而的去想,S表达式的“谓主宾”形式,其中最为重要的其实是“谓语”,一个表达式的关键含义,也均是由首位的“谓语”来阐述的。这个“谓语”能表达和涵盖的意思越多,也就越让人可以更容易的读懂整个表达式的含义。
比如你可以在这个“谓语”里指明“主”“宾”或者更加具体的位置单词的含义。理想情况下,比如这个“谓语”是一个图画,这个图画明确的表达出来了图画后面字符以及图画执行后的输出结果的数量、形式、类型等等。如此,整个括号里面的内容就会变得异常的容易解读了。
1.5 中文的优势
而提到中文就不得不说,汉语的文字,本身就是由图画演变而来的,天然的在阅读和理解上比英文更加有优势。
中文相较英文,有办法用短的长度表达更丰富的内容。英文因为有语法的存在,相较中文只有左右维度缺少上下维度,因此在空间利用上,肯定是不如中文的。
显而易见的,中文相较于英文,可以用更短的空间表达出更多有意义的含义。
It’s very obvious to notice that comparing to English, Chinese can use less space to express much more meaningful connotations.
另外,对于函数式编程语言,其世界仿佛是平的,所有被命名的数据亦或是待使用的函数,好像都生活在同一个空间里。因此这些名字越是有逻辑,就越能体现出它和周围其他名字的关联关系,对使用者而言也就越有意义。
据我调查思考,汉语在语法上更加自由,在语素的颗粒度上更加精细,在书写表达上更有上千年的实践和文化积淀(简练的文言文所淬炼出来的汉字含义,古诗词的工整对账所催生出来的同义词反义词,庞大的地区方言所延展出的在使用上的灵活性),另更有近代吸收西方文化后的白话文改革和简体中文、拼音、标点符号的引入。
上所述种种,更加使用人口之众,都让我认为,将LISP的语法寄宿在汉语下,将拥有强大表达力的中文融入在S表达式幽幽的括号中所产生的编程语言,会将人机交互带入另一层境界......
1.6 中文编程语言之汉字前瞻
此章节为我部分实现了名语言,催生出了新的想法后的新增:
我在实现名语言的时候,受现代汉语的影响,大部分的情况下,只是在尝试如何把不同的常用汉字做组合表达。因为现代汉语是在新文化运动的思潮下所形成的,可以说是很大程度上的受了英文这种在表达上很简易的语言(形式层面上)的影响所形成的,而在名语言中我常常要追求用单字来命名概念,因为这样子会更加的简洁明了(语义层面上),其实这很大程度上的就是古文言文的特色。
我因此常常的需要去查找一些古代汉字的含义和用法,这让我的关注点延伸到了汉字在其内部结构上是如何做组合表达的(汉字的造字法),以及不同汉字是如何通过共用偏旁来体现事物之间的内在联系的(期间还去研究了一下郑码输入法)。
我因而发现,其实可以借用汉字的造字法,创造一些汉字来命名编程语言内部的原有概念。创造汉字来命名这些概念的过程中,天然的会共用到一些偏旁。这些被创造出来的汉字,也天然的更能体现出编程语言内部的这些概念相互之间的关联关系。
如果我们撇掉汉字的概念不谈,那这其实相当于创造了一个字符系统。然后我们用这个字符系统作为人机交互的中间语言,而这个中间语言也就是人类使用编程语言的API。
这样讲太抽象,也体现不出来采用汉字造字法来命名编程语言原有内部概念的优势,我另有举例说明如何选用或采用汉字的造字法来新造汉字去表达LISP编程语言里面的一些固有概念。
1.7 中文化一角
此章节为我部分实现了名语言后的新增:
我在序的《如何改进LISP的难读性》一章节中,曾谈到说把LISP中文化是很有意义的,中文可以改进LISP的难读性并为其带来新的活力。本章节即是我结合之前所说,所实现的一部分名语言的代码。
1.7.1 pairs
pairs是LISP语言非常基础的一个数据结构,lists、 Dictionaries等更为复杂的数据结构都是它的衍生。
一个pair包含两个数据,构造一个pair的函数是cons。
pair在名语言中,被翻译为“对”(后又改为了双)。“对”在中文中可以是动词(对酒当歌),也可以是名词(成双成对)。因此,可以用“对”这个单字来构造所有跟“pair”有关的数据结构。
对于英文而言,“pair”其实也是可以作为动词使用的,但是英文单词和语法的基石是区分词性,区分词性也更利于意思的表达。
然而对于中文来说,严格区分词性不是中文的惯例。如果需要,比如“对子”则肯定是一个名词了。虽然并非所有的词都是通过加“子”变成名词的,但中文具备这种能力,只是在实际使用中没有做完全发挥。
通常情况下,对于编程语言来说,一旦有了一个中心意义的实体,就会在编程中因为各种情况需要不断的围绕它定义名字。中文强大的造词能力所能定义出来一些相关词汇集合,从这些词汇的构成上天然的就能看出他们之间所隐含存在着的一些关联和衍生关系,这对于程序员记忆和理解代码是有极大帮助的。
1.7.2 make-list和build-list
我们以两个Racket标准库里面的例程(Procedures):make-list和build-list为例来尝试阐述所说。
1.7.2.1 原英文
从他们的英文名字上,我们很容易得出这两个例程都是用来创建“list”的(名语言称之为“”)。
- 对于make-list有:很容易明白,它是用来创建一个包含n个相同值的的。
- 对于build-list有:
> (build-list 5 values) '(0 1 2 3 4)
; 生成一个包含0-4这个5个数的。 > (build-list 10 values) '(0 1 2 3 4 5 6 7 8 9)
; 生成一个包含0-9这个10个数的。 > (build-list 10 add1) '(1 2 3 4 5 6 7 8 9 10)
; 生成一个包含0-9这个10个数的,并且每一个数都加上1。 > (build-list 10 (lambda (e) (* e e))) '(0 1 4 9 16 25 36 49 64 81)
; 生成一个包含0-9这个10个数的,并且每一个数都是自身的平方。 build-list的行为更加复杂些:它是用来生成一个,这个包含从0数起的n个数,并且生成的时候,这些数是可以被做一些附加处理的。
对于make-list和build-list这两个例程的具体作用,从名字上我们仅仅能看出它是用来创建的,并不能看出它的其它更具体的作用。
1.7.2.2 中文化后
中文化后,make-list叫“复”,又进而简化为单字“”,意为内部的每个元素是重复的;build-list叫“序”,又进而简化为单字“”,意为内部的元素是按照一定的顺序排列的。
第一,显然这两个例程的名字有着较原英文更加丰富的含义,这是我上文提到的我所说的中文化的意义;
第二,古中文即文言文中,字词的词性很弱,或者说是常常有名词活用动词,动词活用名词的用法。故此,“”这个字是可以被用作动词的,意为创建、起来一组数据、将一组数据存在结构中。总之,字词的表意性因其所处的语境而可以有被活用的留白;
第三,从整体性的角度讲,和其他例程名放在一块,名字显得有更加有规律可循,语言使用者更容易从整体的角度出发获取到更多意义上的理解。
当然你可以说,这其中的“第一”是因为这两个例程原本的英文名字起的不好。
但我们更加应该看到的是,这本质上反应的是一种文化上的差异:英文世界里是存在着一种把事情都简单化的趋向的,这是我认为这两个例程都采用了make-list和build-list的根本原因,我们即使找出更加贴切的英文词汇来作为这两个例程的名字,但或许这些词汇在实际的实现上看起来都是不够直观的。
但对中文来讲就不一样了,中文的造词能力更强:支撑中文造词能力强的技术层原因是它的单字表意特性;非技术层原因是它所造出的词是更加容易被接收者所理解的(这可能和它字的表象特性有关)。
> ( 'val 'val 'val) '(val val val)
> ( 'val 3) '(val val val)
> ( 1 2 3 4 5) '(1 2 3 4 5)
> ( 5 ) '(1 2 3 4 5)
> ( 5 (λ (n) ( 'val n))) '(() (val) (val val) (val val val) (val val val val))
1.8 如何做
Racket语言,编程语言设计和实现的平台,可先用它设计一个方言,并逐步汉化翻译已实现了的内部库。
在如上的过程中,逐步建立自动化代码翻译工具,以便让更多人更容易的投入到翻译中。
循环往复,另可在翻译中引入切合汉语文化的有切实意义的新特性和语法糖。
1.9 释惑
名语言的目的是为了探索以汉语的博大精深,兼容并包,是否可以让编程语言以另外一种方式变得更容易被人理解。
为什么选择LISP/Racket语言实现:第一,我认为LISP的语法特性间接的做了很大程度的留白,这得以让中文可以最大程度的发挥出它的优势。第二,是认为scheme语言的特殊性,其高级抽象形态的实现都可以回溯到最基本的函数定义上,这也得以让人可以循环渐进的替换完其固有的内部英文定义。
名会秉持实事求是的精神,不会采用通俗的计算机中文中已有的通用翻译作为编程语言关键字,而是在兼顾汉语的古今含义上做出折取选择。故此,其字词的选择不会一味的去追求完全的文言文化,也不会追求完全的白话文化,甚至不会追求完全汉化(西文字母、标点符号所独有的优点不容被忽视)。
另外我认为,名的实现过程,除去代码上的东西外,会类似于一个汉语地方方言的实现。所谓方言,比如河南话更甚于说是吴语,即是选择了汉语中的部分字词,定义出了自己方言的特殊意义。而名的实现,我认为会类似于这样一个过程,只不过是在选择时,需要兼顾编程和相应的英文词汇而翻译。
名的其中之重要一目的是为了给汉语编程探索和指明道路。