五个编程语言设计的失误
在近几年来,编程语言的设计正在经历着类似于“文艺复兴”的过程,这么说主要是基于下面两个事实:1)多核技术推动着PC消费者更多的关注并行程序。2)动态语言的性能越来越好,其性期已经可以足够用来实现互联网服务,并且它们正在走出“脚本语言”阴影。
这篇文章试图收集最重要的编程语言的设计错误,以便让那些程序语言设计者们在设计新型的编程语言时避免。我避免了一些纠缠不清的有好有坏的问题,如:动态类型或是静态类型。我也省略了那些看起来并不严重,很容易被修改的错误。例如,加入“参量”(Parametric Type),这在Java中已经有了。Sun在发布Java 1.0版后的第八年才加入了这一功能。还有一个最近的例子是 Google Go Language Design FAQ 中说到的:: “Generics may well be added at some point. We don’t feel an urgency for them, although we understand some programmers do.”
0. Null 指针
几乎在所有的主流编程语言中,对一个对像的引用可能会是一个空指针,这个错误会引发运行时错误。 C.A.R. Hoare 最近声明向这一“发明”负责,尽管如此,其它许多的设计者们都应该对这样的设计受到批评。下面是 C.A.R Hoare 的“忏悔”:
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] More recent programming languages like Spec# have introduced declarations for non-null references. This is the solution, which I rejected in 1965. – C.A.R. Hoare
我把它叫做“亿万美元错误”。这个空指针的发明创造来自1965年。…… 现在的编程语言引入了“非空引用”的声明规格。这个方案被我在1965年给拒绝了。
其它语言,如 C/C++ 更夸张,它们在运到这样的错误时,直接Crash掉,而 Java, Python 和其它语言会抛出一NullPointerException异常,但问题是,这个 RuntimeException 可能会被几乎所有的语句抛出。其实,只需要一个静态类型的语言就可以保证不会出现空指针或空引用。例如: Cyclone 是一个安全的C变种,其引入了非空指针和指针运算的限制。
一些语言甚至让你根本不可能创建空指针,虽然这使得明确的指针不能行进行运算。Haskell 就是这样的一个语言,其提供了Maybe Monad,其强制程序员考虑“Null”的情形。
1. 很难解析的语法
编程语言的语法应该来自 LALR 或是更好的 LL(1)。今天的程序员需要适当的工具来支持其开发语言,也就是我们常说的IDE,编译器或是其它可以帮你解析程序语言的编程工具。这并不会出现在一个单一的前端。也许,多重编译器已经被实现出来了。这可能让我们的开始变得更容易一些。然而,我们现实中的一个反例是 C++,几乎没有哪个C++的编译器可以把C++这个语言完美地正确地解释出来,而且不同C++的编译器的行为如此的诡异。编程语法的开销是微不足道的,程序员应该在编写程序中享有更快速和高效的回报。
2. 未定义的语义
别在语言规格中说“实现规范”!尽可能的少使用“未定义”这样的术语来描述语言的行为(C/C++中出现了很多undefined的行为)!黄金准则是StandardML,其是一个完整地正式的语义。C 语言是这样一个反例,其规则中有太多太多的未定义的情况。然而,由于其广泛使用,所以某些行为的定义已经成为了世界的共识(江湖的行规,或,潜规则)。 举个例子,在C中,整型 overflow 的行为是未定义的,而编译器也是有能力推断出“ x < x+1 ”是否总是为真。不幸的是,这个本来是编译器应该干的事,交给了程序员,于是在C的世界里,出现了大量的整型溢出的代码。而当整型溢出的时候,几乎所有的行为都是像x86处理器一样(如: maxint+1 == minint)。
明确的语义可以让验证和错误检查更容易。虽然,软件校验来得比缓慢,但一定会来。我可以想像,编程语言的下一个机会将会是更容易地校验,这可能需要十到二十年的时间,但今天开始这样做的语言将会在那天成为世界的主流。
3. 坏的Unicode 支持
程序中几乎都要处理字符串,但别忘了并不是所有人都会使用英语来编程。今天,几乎所有的编程语言都不支持Unicode,所以,我们只能使用ANSI的英语来编程。这个时代, 程序员应该使用Unicode 来编程,所以,源代码也可以声明其用什么来编码。
在文本和字节序间的转换和区分在的标准库方面会比语言方面更是一个问题,当然,这也影响了语法。读一读 Python 3 是怎么解决这个问题可能会更有一些帮助。
4. 预处理器
像C++和MP4的预处理器已经被广泛地使用着,使用预处理器更像是一种hack而不是一个干净的解决方案。 他们被用来,使用外部文件(如头文件,但确没有正确地模块机制),使用条件编译,宏替换,等。把这些功能与编程语言集成起来一起使用可以增加程序的性能和开发效率,并没有什么不好的地方。
如果要举一个反例,那么就是预编译器的模块化系统。C使用#include 而 C++ 更痛苦,因为模板需要写一个大的头文件,而且其会被包含在几乎所有的其它文件中。而一个真正的模块化的系统是不需要使用 extern 关键字,也不需要程序的链接,而应该是直接使用。
文章:来源
转载于酷壳CoolShell 无删改 仅以此纪念陈皓(左耳朵耗子)
想裸装 Ubuntu server ,然后 docker 里面跑一切 可以,数据盘/目录 mount 到容器里。 没懂你这样做的目的是啥?有什么 nas 软件是不可替代的?…
人在学校,已经用 VPN 和家里的 NAS 连上了。但是毕竟受限于家里的上行小水管,感觉还是不如千兆内网来得爽快。 因为几块大硬盘和连接线什么的都在家没带到学校,但是手头有一个…
华为手机飘到巴西,一加手机飘到黑龙江。而且很奇怪,仅仅只有手机自带的天气与谷歌地图飘了那么远,其他的 app 都定位正常。更奇怪的是出现这种故障时当场使用 GPS test a…