backup

“One语言”是不存在的

作者:王垠


昨天心血来潮,把我对一种“终极语言”的很多方面想法记录了下来。然而事后我却发现挺多矛盾的地方,现在我已经很难想象这样的语言能够成为“唯一”的程序语言。对人脑的工作原理也纯属我自己的臆测,没有经过任何科学实验的检验。

我是一个很矛盾的人,我怀疑别人,也怀疑自己。有时候想通了的问题,后来却又忘记了。其实很早的时候,我想出了一个很“哲学”理由,说明为什么一种语言是不足以满足所有人的需要的。可是不知道怎么到后来又推翻了自己,写出了为什么世界上只需要一种语言的理由。

那么现在我就来回顾一下我之前的想法,为什么一种语言是永远不够用的。

我们都知道,程序语言里包含了变量,数字,对象,函数等“元素”。它们就像物理学的基本粒子一样,可以用于构造我们所需要的几乎任何“模型”。既然所有的东西都是用基本粒子组成的,那么除了物理学,我们为什么还要有化学和生物?化学家使用的语言是化学元素,它们比基本粒子大很多。生物学家的语言就更大一些了,处于细胞的级别。那么为什么化学家和生物学家不使用基本粒子来描述他们的领域呢?

那是因为基本粒子无法提供足够的“抽象”。它们到底是如何组成原子,原子又如何能产生细胞,这些事情到现在还没搞清楚。用基本粒子来表示化学和生物学,那么我们恐怕要等很久很久以后才能描述化学和生物的现象和原理。

同样的,变量,数字,对象,函数等语言的要素。也是不足以表达我们需要的所有程序的。有人认为函数是这些元素的“终极粘合剂”,可是函数的结构组合能力却不是万能的。函数接受一些参数,返回一个结果。然而有些我们需要表达的概念却不是这样的结构,比如一个带有多根电线的黑匣子,它可以从任何电线输入东西,然后从剩下的电线输出。每根电线的输入和输出方式,在不同的“调用”可以随意的更改。比如,电线 A 第一次被调用是输入,第二次被调用就变成了输出。

这个黑匣子就是逻辑语言(比如 Prolog,miniKanren)的基本元素。你如何用函数来表示它呢?你不能。因为不管你怎么把函数组合起来,这个黑匣子的电线不是连接到函数的输入,就是连接到输出。所以它们总是有“固定”的方向,不能满足这种奇怪的黑匣子的工作方式。

所以一旦出现了这种东西,我们就需要为语言加入新的元素。这个元素可以用更加基本的元素(比如变量等)组成,然而函数却不能作为这里的粘合剂。这个结构必须经过一个更加剧烈的转化,才能实现我们想要的功能。

在这种情况下,我们可以写一个解释器,用来描述这个黑匣子的工作方式。黑匣子被作为一个普通的数据结构,输入解释器,然后我们从解释器得到它的结果。这貌似是万能的方式。

另外一种万能的方式是使用宏(macro)。宏可以把这个黑匣子的“语言描述”(也就是AST)拆散,然后组成一个用原来的语言元素组成的结构,并且把它插入到原来的程序里面。这就相当于把黑匣子“编译”成了我们已有的语言,然后“嵌入”。比如对于逻辑语言的黑匣子,当我们在调用它的时候,宏就会知道它的输入和输出的“方向”。一旦知道了这个方向,它的行为方式就会像一个函数,所以我们就可以以此把它编译成函数。在下一个调用的地方,输入输出的方向又有不同,所以就把它编译成另外一个函数。

所以,每一个宏其实就是一个编译器。你可以用 Lisp/Scheme 的宏来实现几乎任何语言结构。这种“嵌入式语言”通常被叫做 EDSL (Embedded Domain Specific Language)。

有些其它语言也提供一些构建 EDSL 的能力,比如 Haskell, Scala 等。然而它们构建 EDSL 的能力,却不能达到 Lisp/Scheme 宏的地步。它们往往使用“重载”的方式来定义一些操作符,比如"+"号,然后把这些操作符作用于这个 EDSL 的 AST 所特有的类型,从而让操作符“自动切换”到这个 EDSL 的语义。

Haskell 有一个 EDSL 叫 Accelerate 就是这样实现的,它使用 type class 来重载操作符,用于 GPU 的操作。然而我却发现使用它的时候,有时候我必须打进一些莫名其妙的,跟我想要表达的概念毫无关系东西。这是因为重载的能力是有限的,它并不能像宏一样,可以任意的拆解和拼装整个表达式。所以 Accelerate 在很多时候需要你写一些特定的东西,这样它才能避免歧义,实现正确的重载操作。到后来,我发现这些多余的符号成为了非常碍眼的东西,它们让我无法直接的看到我所要表达的概念。

所以我觉得 Lisp 和 Scheme 的宏是很重要的东西。然而我的观点是,宏一定要少用,要在非常有必要的时候才定义宏。否则你的语言里就会出现很多奇怪的结构,这些结构没法用函数调用的语义来理解。这样就造成了程序员之间交流的障碍。在 Common Lisp 里面有很多种 looping macro 就这样的例子,它们让 Common Lisp 的程序难以理解。


评论
热度(8)

© backup | Powered by LOFTER