在近幾年來,編程語言的設(shè)計(jì)正在經(jīng)歷著類似于“文藝復(fù)興”的過程,這么說主要是基于下面兩個(gè)事實(shí):1)多核技術(shù)推動(dòng)著PC消費(fèi)者更多的關(guān)注并行程序。2)動(dòng)態(tài)語言的性能越來越好,其性期已經(jīng)可以足夠用來實(shí)現(xiàn)互聯(lián)網(wǎng)服務(wù),并且它們正在走出“腳本語言”陰影。
這篇文章試圖收集最重要的編程語言的設(shè)計(jì)錯(cuò)誤,以便讓那些程序語言設(shè)計(jì)者們?cè)谠O(shè)計(jì)新型的編程語言時(shí)避免。我避免了一些糾纏不清的有好有壞的問題,如:動(dòng)態(tài)類型或是靜態(tài)類型。我也省略了那些看起來并不嚴(yán)重,很容易被修改的錯(cuò)誤。例如,加入“參量”(Parametric Type),這在Java中已經(jīng)有了。Sun在發(fā)布Java 1.0版后的第八年才加入了這一功能。還有一個(gè)最近的例子是 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.”
幾乎在所有的主流編程語言中,對(duì)一個(gè)對(duì)像的引用可能會(huì)是一個(gè)空指針,這個(gè)錯(cuò)誤會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤。 C.A.R. Hoare 最近聲明向這一“發(fā)明”負(fù)責(zé),盡管如此,其它許多的設(shè)計(jì)者們都應(yīng)該對(duì)這樣的設(shè)計(jì)受到批評(píng)。下面是 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
我把它叫做“億萬美元錯(cuò)誤”。這個(gè)空指針的發(fā)明創(chuàng)造來自1965年。…… 現(xiàn)在的編程語言引入了“非空引用”的聲明規(guī)格。這個(gè)方案被我在1965年給拒絕了。
其它語言,如 C/C++ 更夸張,它們?cè)谶\(yùn)到這樣的錯(cuò)誤時(shí),直接Crash掉,而 Java, Python 和其它語言會(huì)拋出一NullPointerException異常,但問題是,這個(gè) RuntimeException 可能會(huì)被幾乎所有的語句拋出。其實(shí),只需要一個(gè)靜態(tài)類型的語言就可以保證不會(huì)出現(xiàn)空指針或空引用。例如: Cyclone 是一個(gè)安全的C變種,其引入了非空指針和指針運(yùn)算的限制。
一些語言甚至讓你根本不可能創(chuàng)建空指針,雖然這使得明確的指針不能行進(jìn)行運(yùn)算。Haskell 就是這樣的一個(gè)語言,其提供了Maybe Monad,其強(qiáng)制程序員考慮“Null”的情形。
編程語言的語法應(yīng)該來自 LALR 或是更好的 LL(1)。今天的程序員需要適當(dāng)?shù)墓ぞ邅碇С制溟_發(fā)語言,也就是我們常說的IDE,編譯器或是其它可以幫你解析程序語言的編程工具。這并不會(huì)出現(xiàn)在一個(gè)單一的前端。也許,多重編譯器已經(jīng)被實(shí)現(xiàn)出來了。這可能讓我們的開始變得更容易一些。然而,我們現(xiàn)實(shí)中的一個(gè)反例是 C++,幾乎沒有哪個(gè)C++的編譯器可以把C++這個(gè)語言完美地正確地解釋出來,而且不同C++的編譯器的行為如此的詭異。編程語法的開銷是微不足道的,程序員應(yīng)該在編寫程序中享有更快速和高效的回報(bào)。
別在語言規(guī)格中說“實(shí)現(xiàn)規(guī)范”!盡可能的少使用“未定義”這樣的術(shù)語來描述語言的行為(C/C++中出現(xiàn)了很多undefined的行為)!黃金準(zhǔn)則是StandardML,其是一個(gè)完整地正式的語義。C 語言是這樣一個(gè)反例,其規(guī)則中有太多太多的未定義的情況。然而,由于其廣泛使用,所以某些行為的定義已經(jīng)成為了世界的共識(shí)(江湖的行規(guī),或,潛規(guī)則)。 舉個(gè)例子,在C中,整型 overflow 的行為是未定義的,而編譯器也是有能力推斷出“ x < x+1
”是否總是為真。不幸的是,這個(gè)本來是編譯器應(yīng)該干的事,交給了程序員,于是在C的世界里,出現(xiàn)了大量的整型溢出的代碼。而當(dāng)整型溢出的時(shí)候,幾乎所有的行為都是像x86處理器一樣(如: maxint+1 == minint)。
明確的語義可以讓驗(yàn)證和錯(cuò)誤檢查更容易。雖然,軟件校驗(yàn)來得比緩慢,但一定會(huì)來。我可以想像,編程語言的下一個(gè)機(jī)會(huì)將會(huì)是更容易地校驗(yàn),這可能需要十到二十年的時(shí)間,但今天開始這樣做的語言將會(huì)在那天成為世界的主流。
程序中幾乎都要處理字符串,但別忘了并不是所有人都會(huì)使用英語來編程。今天,幾乎所有的編程語言都不支持Unicode,所以,我們只能使用ANSI的英語來編程。這個(gè)時(shí)代, 程序員應(yīng)該使用Unicode 來編程,所以,源代碼也可以聲明其用什么來編碼。
在文本和字節(jié)序間的轉(zhuǎn)換和區(qū)分在的標(biāo)準(zhǔn)庫(kù)方面會(huì)比語言方面更是一個(gè)問題,當(dāng)然,這也影響了語法。讀一讀 Python 3 是怎么解決這個(gè)問題可能會(huì)更有一些幫助。
像C++和MP4的預(yù)處理器已經(jīng)被廣泛地使用著,使用預(yù)處理器更像是一種hack而不是一個(gè)干凈的解決方案。 他們被用來,使用外部文件(如頭文件,但確沒有正確地模塊機(jī)制),使用條件編譯,宏替換,等。把這些功能與編程語言集成起來一起使用可以增加程序的性能和開發(fā)效率,并沒有什么不好的地方。
如果要舉一個(gè)反例,那么就是預(yù)編譯器的模塊化系統(tǒng)。C使用#include
而 C++ 更痛苦,因?yàn)槟0逍枰獙懸粋€(gè)大的頭文件,而且其會(huì)被包含在幾乎所有的其它文件中。而一個(gè)真正的模塊化的系統(tǒng)是不需要使用 extern
關(guān)鍵字,也不需要程序的鏈接,而應(yīng)該是直接使用。