Welcome

首页 / 软件开发 / 数据结构与算法 / 如何设计一门编程语言(一) 什么是坑(a)

如何设计一门编程语言(一) 什么是坑(a)2014-08-23这个系列的起因是这样的,王垠写了一篇喷go的博客http://www.yinwang.org/blog-cn/2013/04/24/go-language/,里面说go已经烂到无可救药了,已经懒得说了,所以让大家去看http://www.mindomo.com/view.htm?m=8cc4f95228f942f8886106d876d1b041,里面有详细的解释。然后这篇东西被发上了微博,很多博友立刻展示了人性丑陋的一面:

1、那些go的拥护者们,因为go被喷了,就觉得自己的人格受到了侮辱一样,根本来不及看到最后一段的链接,就开始张牙舞爪。

2、王垠这个人的确是跟人合不来,所以很多人就这样断定他的东西“毫无参考价值”。

不过说实话,文章里面是喷得有点不礼貌,这也在一定程度上阻止了那些不学无术的人们继续阅读后面的精华部分。如果所有的文章都这样那该多好啊,那么烂人永远都是烂人,不纠正自己的心态永远获得不了任何有用的知识,永远过那种月入一蛆的日子,用垃圾的语言痛苦的写一辈子没价值的程序。

废话就说到这里了,下面我来说说我自己对于语言的观点。为什么要设计一门新语言?原因无非就两个,要么旧的语言实在是让人受不了,要么是针对领域设计的专用语言。后一种我就不讲了,因为如果没有具体的领域知识的话,这种东西永远都做不好(譬如SQL永远不可能出自一个数据库很烂的人手里),基本上这不是什么语言设计的问题。所以这个系列只会针对前一种情况——也就是设计一门通用的语言。通用的语言其实也有自己的“领域”,只是太多了,所以被淡化了。纵观历史,你让一个只做过少量的领域的人去设计一门语言,如果他没有受过程序设计语言理论的系统教育,那只能做出屎。譬如说go就是其中一个——虽然他爹很牛逼,但反正不包含“设计语言”这个事情。

因此,在21世纪你还要做一门语言,无非就是对所有的通用语言都不满意,所以你想自己做一个。不满意体现在什么方面?譬如说C#的原因可能就是他爹不够帅啦,譬如说C++的原因可能就是自己智商太低hold不住啦,譬如说Haskell的原因可能就是用的人太少招不到人啦,譬如说C的原因可能就是实在是无法完成人和抽象所以没有linus的水平的人都会把C语言写成屎但是你又招不到linus啦,总之有各种各样的原因。不过排除使用者的智商因素来讲,其实有几个语言我还是很欣赏的——C++、C#、Haskell、Rust和Ruby。如果要我给全世界的语言排名,前五名反正是这五个,虽然他们之间可能很难决出胜负。不过就算如此,其实这些语言也有一些让我不爽的地方,让我一直很想做一个新的语言(来给自己用(?)),证据就是——“看我的博客”。

那么。一个好的语言的好,体现在什么方面呢?一直以来,人们都觉得,只有库好用,语言才会好用。其实这完全是颠倒了因果关系,如果没有好用的语法,怎么能写出好用的库呢?要找例子也很简单,只要比较一下Java和C#就够了。C#的库之所以好用,跟他语言的表达能力强是分不开的,譬如说linq(,to xml,to sql,to parser,etc),譬如说WCF(仅考虑易用性部分),譬如说WPF。Java能写得出来这些库吗?硬要写还是可以写的,但是你会发现你无论如何都没办法把他们做到用起来很顺手的样子,其实这都是因为Java的语法垃圾造成的。这个时候可以抬头看一看我上面列出来的五种语言,他们的特点都是——因为语法的原因,库用起来特别爽。

当然,这并不要求所有的人都应该把语言学习到可以去写库。程序员的分布也是跟金字塔的结构一样的,库让少数人去写就好了,大多数人尽管用,也不用学那么多,除非你们想成为写库的那些。不过最近有一个很不好的风气,就是有些人觉得一个语言难到自己无法【轻松】成为写库的人,就开始说他这里不好那里不好了,具体都是谁我就不点名了,大家都知道,呵呵呵。

好的语言,除了库写起来又容易又好用以外,还有两个重要的特点:容易学,容易分析。关于容易学这一点,其实不是说,你随便看一看就能学会,而是说,只要你掌握了门道,很多未知的特性你都可以猜中。这就有一个语法的一致性问题在里面了。语法的一致性问题,是一个很容易让人忽略的问题,因为所有因为语法的一致性不好而引发的错误,原因都特别的隐晦,很难一眼看出来。这里我为了让大家可以建立起这个概念,我来举几个例子。

第一个例子是我们喜闻乐见的C语言的指针变量定义啦:

int a, *b, **c;

相信很多人都被这种东西坑过,所以很多教科书都告诉我们,当定义一个变量的时候,类型最后的那些星号都要写在变量前面,避免让人误解。所以很多人都会想,为什么要设计成这样呢,这明显就是挖个坑让人往下跳嘛。但是在实际上,这是一个语法的一致性好的例子,至于为什么他是个坑,问题在别的地方。

我们都知道,当一个变量b是一个指向int的指针的时候,*b的结果就是一个int。定义一个变量int a;也等于在说“定义a是一个int”。那我们来看上面那个变量声明:int *b;。这究竟是在说什么呢?其实真正的意思是“定义*b是一个int”。这种“定义和使用相一致”的方法其实正是我们要推崇的。C语言的函数定义参数用逗号分隔,调用的时候也用逗号分隔,这是好的。Pascal语言的函数定义参数用分号分隔,调用的时候用逗号分隔,这个一致性就少了一点。

看到这里你可能会说,你怎么知道C语言他爹就是这么想的呢?我自己觉得如果他不是这么想的估计也不会差到哪里去,因为还有下面一个例子:

int F(int a, int b);

int (*f)(int a, int b);

这也是一个“定义和使用相一致”的例子。就第一行代码来说,我们要如何看待“int F(int a, int b);”这个写法呢?其实跟上面一样,他说的是“定义F(a, b)的结果为int”。至于a和b是什么,他也告诉你:定义a为int,b也为int。所以等价的,下面这一行也是“定义(*f)(a, b)的结果为int”。函数类型其实也是可以不写参数名的,不过我们还是鼓励把参数名写进去,这样Visual Studio的intellisense会让你在敲“(”的时候把参数名给你列出来,你看到了提示,有时候就不需要回去翻源代码了。

关于C语言的“定义和使用相一致”还有最后一个例子,这个例子也是很美妙的:

int a;

typedef int a;

int (*f)(int a, int b);

typedef int (*f)(int a, int b);