透明編譯
2013 年 2 月 12 日
越來越多的網路開發人員使用 CoffeeScript 和 SCSS 等語言,這些語言編譯成其他在瀏覽器中執行的文字來源語言。此類來源到來源編譯器(也稱為轉譯器 [1])並非新鮮事,Cfront 在 C++ 早期廣泛用於產生目標 C 程式碼。但對我來說,有一個差異點出 CoffeeScript 和 SCSS 是透明編譯器
使用大多數編譯器時,您不太關心下游產生什麼。只要它遵循來源語言的語意,它實際上就是一大塊位元。但如果您要為瀏覽器產生 JavaScript,這種無知就難以忍受。除錯環境現今變得相當靈巧,但它們都以 HTML/CSS/JavaScript 三元組為基礎。因此,您了解輸入語言如何轉換成其可執行目標非常重要。
此限制對來源語言有很大的影響。您需要確保輸出與來源非常清楚地對應。當我撰寫此 CoffeeScript 時
$(window).on 'touchTap', (event) -> window.touchPanel.tap(event)
我可以在瀏覽器除錯器中輕鬆辨識結果 JavaScript
$(window).on('touchTap', function(event) { return window.touchPanel.tap(event); });
即使對於 CoffeeScript 轉換中更複雜的部分,例如將
runSetupBuild: (slide, positionClass) -> switch positionClass when 'current', 'next' @buildsFor(slide)?.setupBuild?.forwards() # ...
轉換成
Infodeck.prototype.runSetupBuild = function(slide, positionClass) { var _ref, _ref1, _ref2, _ref3; switch (positionClass) { case 'current': case 'next': return (_ref = this.buildsFor(slide)) != null ? (_ref1 = _ref.setupBuild) != null ? _ref1.forwards() : void 0 : void 0; /* ... */
這仍然成立。在該轉換中發生了很多事,但對應關係仍然相當清楚。如果我需要在該程式碼中除錯,我可以輕鬆看出它如何與來源 CoffeeScript 相符。這正是編譯程序透明的本質 - 您將在輸出語言中工作的意圖 [2]。
相反地,有源碼到源碼編譯器並不要求您使用輸出語言工作,或將輸出語言的可見性視為一種不幸的暫時機制。這些仍然可能很有用,您可以在 JavaScript 世界中看到它們,例如 Dart、GWT 和 ClojureScript。這正是意圖上的差異,將透明的轉譯風格與更常見的方法區分開來。[3]
您必須努力保持編譯透明的事實限制了您在源語言中可以執行的操作。在語言結構中,您沒有與更不受約束的編譯形式相同的自由度。您必須遵循目標語言的基本語意,並保持大致相同的程式結構。這些限制尚未被廣泛討論為語言設計的一個特徵。
CoffeeScript 說明了儘管有這些限制,您仍然可以在源語言和目標語言之間獲得相當大的語法差異。CoffeeScript 在語法上感覺更像是 Python,而不是類似 C 的 JavaScript。這種語法變異並非總是如此,事實上,有一組重要的透明編譯語言子集力求成為目標語言的超集。SCSS 和 TypeScript 屬於此類別 - 任何 CSS 表達式在 SCSS 中都是有效的。使用超集語言使對應關係更加清晰,我認為這非常適合 CSS,因為 CSS 的語法運作良好,但語言缺少一些方便的功能。
有些人說使用透明編譯幾乎沒有意義 - 如果您必須了解目標程式碼進行偵錯,那麼使用不同的源有什麼價值?對我來說,價值在幾個方向。首先,這是一種在目標語言中獲取遺失的有用語言功能的方法。SCSS 為我提供了方便的功能,例如變數(因此我可以說 $light-purple
而不是 #f8c8fe
,並且如果我想調整它,只需在一個地方進行更改即可)。
更激烈的語法變更,例如 CoffeeScript,需要更強的理由。一位同事在完成一個專案後,說得很好。他是一位經驗豐富的 JavaScript 程式設計師,而且這個專案從一開始就寫出有紀律的 JavaScript。因此,他對 JavaScript 程式庫的品質非常滿意。然而,他仍然得出結論,他們在 CoffeeScript 中工作會更好,因為在偵錯產生的 JavaScript 程式碼時,即使在閱讀 CoffeeScript 時,也更容易了解正在發生的事情。對於像上面顯示的那些小片段,轉換可能看起來不是什麼大不了的事。但一旦你處理數百行程式碼,更不用說更多,就會產生很大的差異。[4]
註解
1: 根據使用情況,在我看來,「transpiler」一詞用作 source-to-source 編譯器的同義詞。因此,transpiler 可能透明,也可能不透明。我也看過「source-to-source 轉譯」一詞與「source-to-source 編譯」等價使用。
2: 即使是不透明編譯,也有人會研究輸出。偶爾會出現一些奇怪的行為或錯誤,需要你深入研究編譯器輸出。一些程式設計師喜歡了解編譯器在做什麼,儘管隨著編譯器和虛擬機變得越來越複雜,這已變得不那麼常見。但這種活動是一種例外。
3: 有趣的是,sourcemaps 的開發是否會讓像 coffeescript 這樣的語言遠離透明性。
4: 在處理我的 infodecks 時,我只寫了幾百行 coffeescript,但我同意他的說法,並將繼續對任何非平凡數量的 javascript 使用 coffeescript。