在近幾年來,程式設計語言的設計正在經曆著類似於“文藝複興”的過程,這麼說主要是基於下面兩個事實:(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 指標
幾乎在所有的主流程式設計語言中,對一個對像的引用可能會是一個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
我把它叫做“億萬美元錯誤”。這個null 指標的發明創造來自1965年。…… 現在的程式設計語言引入了“非Null 參考”的聲明規格。這個方案被我在1965年給拒絕了。
其它語言,如 C/C++ 更誇張,它們在運到這樣的錯誤時,直接Crash掉,而
Java, Python 和其它語言會拋出一NullPointerException異常,但問題是,這個 RuntimeException
可能會被幾乎所有的語句拋出。其實,只需要一個靜態類型的語言就可以保證不會出現null 指標或Null 參考。例如: Cyclone
是一個安全的C變種,其引入了非null 指標和指標運算的限制。
一些語言甚至讓你根本不可能建立null 指標,雖然這使得明確的指標不能行進行運算。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
關鍵字,也不需要程式的連結,而應該是直接使用。
英文原文連結:http://beza1e1.tuxen.de/articles/proglang_mistakes.html
譯文連結:http://coolshell.cn/?p=2598