計算機編程語言必須能夠自舉嗎?

設計一種新的計算機編程語言的時候,用新設計的語言編寫語言自己的編譯器或解析器是不是編程語言設計過程中必須的一步?
當有一種新的編程語言火起來的時候, 經常看到有新聞說用該語言重寫了自己的編譯器。這麼做除了證明這個語言可以自舉以外還有什麼意義?
有沒有不能自舉的編程語言(不是指沒有人寫,而是指從理論上就不能)?


有人說:沒有必要自舉,不自舉不代表它沒有這個能力。這話說的沒有太大毛病。但是我們考慮如下兩點:

1、自舉從事實上印證了它(語言+編譯器)具備這個(強大的)能力,不自舉只能在理論上保留它具有這個能力的可能性,兩者不在一個層面上,說服力孰強孰弱不言而喻。新語言在大規模推廣之前,往往欠缺這種說服力,進而導致推廣不利。

2、自舉過程中和自舉之後,核心開發者每天使用自己設計的語言工作(開發自己的編譯器),不斷的在實踐中鍛造,利於及早發現設計缺陷和不足之處,並及時解決;自舉之前,只能每天花費大量的時間和精力,使用其他編程語言開發和維護自己的編譯器,學習積累的都是別的語言的經驗和教訓,缺少在實踐中檢驗自己設計的語言的機會。如果自己設計的語言自己都不去深度地使用,又上哪裡獲取第一手的反饋信息呢,又如何改善呢。

所以自舉越早對編程語言自身發展完善越有利,最好是在自身定型之前儘早自舉。

但是自舉並不是必須的。不自舉,只是失去了一個比較好的檢驗、印證、宣傳自身實力的機會。作者也完全可以通過別的途徑找到類似的機會。


不一定。取決於你的用途。如果是dsl通常不必自舉。如果你的語言不是圖靈完備,或者缺乏好用的指針等其它高級玩意兒,前者往往無法自舉,後者你累死你也很難寫出自舉的程序來。


這是一種宣言。編譯器可以編譯自己,就跟GUI庫可以用來寫開發自己的拖控制項程序一樣,是證明你這個東西已經可以投入使用的有效手段。


對絕大部分語言來說,能自舉,但這不是必須的一步,像lua什麼的自舉真心沒啥必要。

對於可以廣泛應用的、通用性強的語言來說,自舉不錯。比如C/C++。Google也在推進Go語言的自舉。這有利於宣示語言本身的強大。但是這不是通過「自己能夠自舉」來證明的,而是「自舉有足夠的優勢」,這樣才能在這個過程中,展示語言自身的能力。

換句話說:一個能夠有價值自舉的語言,舉舉別的語言也很省心。比如C/C++語言,可以便捷的地實現其他各類編譯器,不僅僅是自身。不過凡事從自身做起,先舉自己吧。

如果自舉並不比用其他語言(類似C/C++這些)實現更加便捷的話,做這種不討好的事情,只能說是一種信仰。至於有沒有不能自舉的語言?可以說能夠處理基本數據結構的語言,都可以自舉並且舉起任何一種語言。除非是數據處理能力非常弱的那些,比如說早期的.bat批處理語言。

btw: 早期C/C++自舉,更多是因為其他語言開發編譯器的確不如用它。它們自舉不是為了宣示強大,而是高效的選擇。


圖靈完備的語言,應該是能夠自舉的。不過能不能自舉是一種性質,要不要真的自舉給你看是另一回事。


coq這種好像連圖靈完全都不是,不照樣好多人用嘛………………


目前一共有三種類型的編程語言:編譯型、解釋型、虛擬機型。
其中,編譯型和虛擬機型語言是可以自舉的,而解釋型語言是不能自舉的。其中,編譯型語言可以完整自舉。不過虛擬機型語言的自舉是不完整的——它不能自己實現自己的虛擬機。

舉個例子,bash腳本是一種解釋型語言,每次執行都需要/bin/bash去讀取每條指令並執行對應的操作。如果bash腳本想要自舉,就必須用腳本去實現本身應該由/bin/bash實現的功能,但是無論腳本怎麼努力,它都不可能做到——因為腳本自身是無法脫離/bin/bash獨立運行的。即使你用bash腳本實現了腳本解釋器,你也必須要另外一個由其他編程語言編寫出的/bin/bash來啟動這個解釋器。這樣根本不能稱為自舉。

再來看虛擬機語言。Java的編譯器是Java寫的,而它的任務是把.java源文件編譯成.class位元組碼。然後,這些位元組碼可以在Java虛擬機中執行。只要有一個java虛擬機和一個事先編譯好的java編譯器,我們就可以把Java編譯器的源代碼由.java編譯成.class,然後在虛擬機中執行這些.class,把其他Java源代碼編譯成.class。這樣就實現了自舉。
不過,Java的自舉並不完整,因為Java語言的.class文件始終無法脫離Java虛擬機獨立運行,而這個虛擬機必須是其他編程語言實現的。但是這確實是一種自舉。如果我們用虛擬化的觀點來看待虛擬機,完全可以把Java虛擬機視為一台特殊的電腦,這台電腦上只能運行Java位元組碼。這樣,只要Java語言實現了能在這台電腦(Java虛擬機)上編譯出用Java寫的Java編譯器,那它就完成自舉了。而這台電腦(Java虛擬機)到底是怎麼製造出來的,自舉的時候可以不關心。比如,不排除未來會有人研發直接運行Java虛擬機指令集的硬體設備。

(說句題外話,其實在x86這樣的複雜指令集cpu里,「機器語言」也是解釋執行的。x86 cpu會把機器語言即時編譯為「微指令」,真正被執行的不是機器語言而是微指令。但是這並不影響x86上的C語言的一種編譯型語言。解釋型語言和編譯型語言的區別在於程序運行的方式,是從源代碼直接運行還是從目標代碼運行,而目標代碼到底是怎樣運行的,與編程語言是解釋型還是編譯型其實沒有關係。)

編譯型語言就不用說了,它們的自舉是最徹底的,只依賴於特定的硬體設備(比如PC機)。不過虛擬化的廣為流行讓「硬體設備」的概念開始變得模糊了。大量真實的操作系統其實並非直接運行在物理計算機上,而是運行在虛擬機里。那麼,在虛擬機中工作的C語言是否是一種「虛擬機型語言」,就變得很有趣了。

特別是,如果我們實現一種特別的虛擬機,它採用的指令集是目前沒有硬體實現的(比如開發一種類似於x86但是與x86不兼容的指令集,以至於為它編譯的程序只能運行在虛擬機中)。然後,我們為該虛擬機實現C語言,那此時,運行在該虛擬機中的C語言就變成了「虛擬機型語言」。有人說應該把虛擬機型語言視為解釋型語言,這是不準確的。比如該例中為虛擬機實現的C語言通常被視為編譯型語言。它與其他編譯型語言的唯一區別是,它的目標指令集是只有軟體實現,而沒有硬體實現的。Java虛擬機正是這樣一種「目前不存在硬體實現」的虛擬機。

從這個角度來看,其實沒有「虛擬機型語言」,虛擬機型語言應該被視為編譯型語言,只是它編譯出來的代碼只能在「用軟體實現的計算機」上運行罷了。
同樣,這個全新的角度也可以解決類似「即時編譯」型語言到底是什麼類型的問題。在這裡,應該把即時編譯型語言視為解釋型語言,因為它產生中間代碼只是為了加快解釋速度,它並沒有中間代碼的輸出,並且也不能從中間代碼直接運行一個程序。

於是,對這個問題的回答也就變成了:
1、解釋型語言不能自舉。解釋型語言是指那些每次都從源代碼開始運行的語言,無論它們運行過程中是否產生中間代碼。因為解釋型語言即使產生了中間代碼也不會保存,下次並不能從中間代碼直接運行程序,因此無法脫離解釋器獨立運行。「用某種解釋型語言去實現它自己的解釋器」看上去就是一個玩笑。雖然確實可以寫出來,但是為了運行該解釋器,我們必須先運行另一個解釋器。然後,如果另一個解釋器也是該語言寫出來的,我們為了運行這個解釋器,就必須先運行下一個解釋器……如果我們希望運行的所有解釋器都是該語言寫出來的,那麼,我們將永遠也無法運行該程序。因此,解釋型語言無法自舉。

2、編譯型語言可以自舉。編譯型語言是指那些在運行前需要先把程序由源代碼轉換成另一種形式,並且只運行轉換後的形式的一種編程語言。無論該語言是運行在通常的計算機硬體上還是特殊的「虛擬計算機」上,只要它能在該運行平台自我編譯,它就實現了自舉。此時,我們可以安全的刪除先前由其他語言寫成的編譯器,只保留自舉編譯器。自舉編譯器可以在該平台獨立運行,並且編譯該編譯器自己的源代碼,從而得到新版本的自舉編譯器。

————附加的內容————

其實,「C語言是編譯型語言」這種說法是不準確的,因為現在確實有C語言解釋器的存在,我就曾經用過一款。運行在該解釋器上的C語言毫無疑問是一種解釋型語言。

同樣的道理,我們也可以編寫一款「bash腳本編譯器」,把bash腳本直接編譯為可獨立運行的目標代碼,從而讓bash腳本變成編譯型語言。

此外還有,Lua這樣的語言既可以從源代碼直接運行,也可以先輸出編譯後的位元組碼,然後從位元組碼運行。所以它既是解釋型語言,又是編譯型語言。

因此,在討論某個特定的編程語言到底是解釋型還是編譯型這個問題時,我們需要結合當前實際的運行方式來確實。不過,這並不是該問題的主題。


推薦閱讀:

為什麼pat考試可選的語言眾多,但提供的編輯器卻少得可憐,甚至非常老舊?
C++命名空間的問題:這是編譯器Bug嗎?
對於stdio.h 之類的頭文件,在C++中使用時,編譯器是怎樣處理的?
Phi node 是如何實現它的功能的?
如何抽象評判現有語言優劣,繼而設計一款別具優雅的計算機語言 X ?

TAG:編程 | 計算機科學 | 編譯器 |