再見 Ruby Thursday
如果您還不知道,Crystal 是一種語法和語義類似於 Ruby 的程式語言,只是它不是直譯的,而是將程式編譯為原生碼。
所以我們選擇用 Ruby 實作 Crystal 的編譯器。為什麼呢?
- Ruby 是一種很棒的語言,具有優雅的語法,適合快速原型開發。
- 有一天,我們可以改用 Crystal 撰寫編譯器,如果語法和語義與 Ruby 相似,移植程式碼應該相對容易。
我們嘗試多次用 Crystal 實作編譯器。但我們總是遇到問題。
編譯時間太長
我們一開始認為,這是因為 Ruby 有時很慢。
然而,在初期,該語言是純粹的,非常、非常類似於 Ruby,您永遠不必指定類型。編譯時間相對於程式碼大小和 Array 實例化的數量呈指數增長。嘗試僅編譯部分編譯器就開始花費數分鐘。我們認為這是不可接受的。
因此,我們做了一個小小的犧牲:有時您必須指定陣列、雜湊和其他泛型類型的類型。
a = [] # OK for Ruby, but not for Crystal
b = [] of Int32 # OK for Crystal
c = [1, 2, 3] # OK for Ruby and for Crystal
c << 4 # OK for Ruby and for Crystal
c << "hello" # OK for Ruby, error for Crystal (c is Array(Int32))
d = [1, 2, 3] of Int32 | String
d << "hello" # OK for Crystal
透過這個小小的變更,編譯時間好得多,相對於程式碼大小呈線性增長。
但我們仍然無法完成新的編譯器。
缺少功能
在用 Ruby 撰寫的編譯器中,我們使用了 Array、Hash 和 Set。我們存取了檔案系統,並使用了 LLVM 的綁定。除非我們的語言和標準程式庫中具有相同的功能,否則我們永遠無法用 Crystal 實作編譯器。
因此,我們在標準程式庫中新增了許多功能。我們新增了 C 的綁定。我們有 C 結構和聯集。我們有函式指標。所有這些都在 Crystal 中指定,不需要用另一種語言撰寫。
但仍然…
錯誤
編譯器並不完美。它有(而且仍然有)錯誤。一切在小範例和我們的測試中都運作良好,但是用 Crystal 撰寫編譯器確實開始測試 Ruby 中的編譯器,並揭示了許多錯誤和缺少的功能。
因此,我們修正了大多數這些錯誤,並新增了缺少的功能。
但所有這些總是意味著
落後於編譯器
我們在用 Ruby 撰寫的編譯器中引入的每個錯誤修正或新功能都必須移植到用 Crystal 撰寫的編譯器(仍然無法成功編譯)。而且我們不時會想在語言中新增新功能,而我們也這樣做了,因此我們開始越來越落後。
因此,有一天我們決定進行功能凍結(以及錯誤凍結,除非我們可以解決該問題)。
這是一條漫長的路,但也非常有趣且富有啟發性
- 將程式碼從 Ruby 移植到 Crystal 非常容易,而且大多數時候只需要進行少量修改。
- 移植後的程式碼的行為與在 Ruby 中完全相同。我們仍然很驚訝我們能夠如此完美地捕捉到 Ruby 的語法和語義。
- 移植後的程式碼揭示了 Ruby 撰寫的編譯器中的錯誤,這表示理論上,Crystal 可以協助您擁有更穩健且正確的程式碼。
而今天,星期四,我們終於成功了。我們設法用 Crystal 本身撰寫了 Crystal 的編譯器。耶!新的編譯器可以成功地編譯自身,而且這個新的編譯器可以編譯自身,而產生的二進制檔案與舊的二進制檔案完全相同。它也可以編譯其規範,而且它們全部都通過了。
Crystal 的未來看起來光明
它(更)快了
用 Ruby 撰寫的編譯器大約需要 20 秒才能推斷出編譯器的類型。用 Crystal 撰寫的編譯器大約需要 2.8 秒才能執行相同的操作。
請記住:我們這裡談論的是全域類型推斷。2.8 秒即可完成編譯器的語義分析。還不錯!
而且我們仍然有許多最佳化可以應用,無論是對編譯器產生的程式碼,還是編譯器和標準程式庫中存在的程式碼(例如,Hash 的實作非常簡單)。
我們不再依賴 Ruby
因為我們現在有一個用 Crystal 撰寫的編譯器,所以我們可以使用 Crystal 編譯編譯器的新版本。再見,Ruby。很高興您加入我們的團隊,但是,嗯,您對於我們的需求來說有點慢。是的,是的,我們很喜歡您很多地方,但是我們這裡談論的是編譯器。您不能讓程式設計師等待數分鐘甚至數分鐘才能編譯他們的程式。哦,也許我們對您有點苛刻。您知道嗎?別走開。回來。您仍然可以與您的朋友 Rails 協助我們開發出色的 Web 應用程式前端。我們是認真的。您在這方面很出色。我們對後端不太確定。Erlang 和 Go 在這方面真的很棒。可惜它們的語法(和語義)不如您的好。有一天會出現像您一樣的語言,但速度像原生碼一樣嗎?可能不會。但是,如果您來自 Ruby,必須做出一些小犧牲的類似語言如何?我們真的希望如此。
路線圖
現在我們必須修正編譯器中剩餘的錯誤。我們不喜歡有錯誤的軟體,因此除非我們使其堅若磐石,否則我們不會繼續在語言中新增功能。
然後我們可以開始考慮並行處理、更好的巨集、更好的函式指標、真實的結構、具名引數、元組、纖程、偵錯器…