內部可重新編程
2013 年 1 月 10 日
我正在編寫程式,想要在我目前輸入的位置上方新增一空白行。我使用的編輯器沒有內建這項功能,而我終於有足夠的渴望,真的想要這項功能。我快速搜尋 Google,找到幾行程式碼,將它們貼到我的啟動檔中,執行它們,然後我就可以在上方用一個按鍵建立空白行。只花了我幾分鐘,我不用安裝任何外掛程式,也不用重新啟動編輯器——這對 emacs 使用者來說是再正常不過的日常工作。
Emacs 是一個古老的軟體,可追溯到 70 年代中期。它允許人們透過修改即時環境輕鬆地擴充它的哲學,與其他一些古老但創新的系統(例如 Lisp 機器和 Smalltalk)相同。
這種哲學現在似乎比較少見。當然有許多可擴充的系統,你可以為 Firefox 等瀏覽器和 Eclipse 等編輯套件安裝外掛程式。整個自由/開放原始碼運動就是要讓你存取執行電腦的程式碼,讓你(理論上)可以盡情調整。但在這些環境中的擴充和你在 emacs 或 Smalltalk 中進行的重新編程之間,有一個明顯的差異。關於如何輕鬆地進行小修改,例如我在上方新增的新指令。這也關於在不離開環境的情況下進行修改——我不會啟動一些獨立的工具鏈來新增 emacs 函數,我在 emacs 本身中工作。
這也與新增某種「巨集功能」的工具不同。新增一個新的 elisp 函數正是 emacs 本身編寫程式的方式——你編寫小擴充和軟體核心編程之間沒有差異。這種統一性讓你能夠深入編輯器的核心。這也表示你的修改不會被貶低到某個「腳本」選單中——它們與工具的任何其他部分沒有區別。
這種能力也是一種關於你如何與你的工具建立關係的哲學。對許多人來說,你使用的軟體是一個相對固定的產品。即使是外掛程式也只會新增一個相對有限的選項選單。內部可重新編程工具允許你新增或變更軟體的任何部分,讓你能夠打造工具,完全符合你的比喻性雙手。
這種思考甚至適用於程式語言本身。Lisp 和 Smalltalk 都是極簡語言,它們讓使用者可以輕鬆地擴充語言,讓擴充看起來與核心相同。這兩種語言都沒有任何特殊語法,用於條件邏輯等基本語言功能。這種彈性讓 Smalltalk 可以新增例外處理,而不需要任何語言變更。
內部重新編程最大的問題之一是,軟體實例會因此而產生碎片化。當我用許多個人函式修改我的 emacs 實例時,我正在建立我自己的自訂 emacs 版本,它與我機器上的 emacs 組態緊密結合。這不可避免地會引發問題,例如如何處理核心應用程式的升級,以及與他人分享我的函式有多容易。
具有外掛程式架構和巨集語言的系統,會藉由減少自訂的表面積來處理這個問題,但正如 Nic Ferrier 所言:「一個可重新編程的系統非常強大。濫用權力總是可能的,而可重新編程系統的一個原則是,人們必須能夠濫用它。」
當然,emacs 社群是一個很好的例子,說明了這在實務上是如何進展的。emacs 已經穩定到,儘管有定期更新,但大多數人仍然可以在沒有嚴重問題的情況下進行升級。emacs 已經使用套件管理系統來協助散布可分享的變更,自從最初的 Emacs 和 Smalltalk 時代以來,在如何分享程式碼方面已經有了很大的進展。分散式版本控制工具的興起,為管理共用程式碼提供了更多想法。
對大多數開發人員來說,他們最接近內部重新編程哲學的,可能是 Unix 命令列殼層。我認為這是一個(簡單的)範例,因為任何人都可以輕鬆地建立一個新指令,並將它新增到系統中,讓它與內建指令沒有區別。套件允許人們新增更多指令,並因此調整他們的命令列環境,以符合他們的特定需求。命令列的互動模式不如 Emacs 和 Smalltalk 豐富,因此它只是那些環境所提供的功能的蒼白反射,但它確實提供了體驗的一絲提示。
如果內部重新編程對於針對程式設計師的工具來說很罕見,那麼對於針對非程式設計師的工具來說就更罕見了。我常常想,這是否應該改變。讓更多工具展現這種品質會帶來什麼結果?這是否會鼓勵更多人學習程式設計,以便更好地控制他們花費大量時間的環境?這肯定是一部分 Alan Kay 對 Dynabook 的願景。他認為孩子不是被動的媒體消費者,而是積極地編寫他們的環境。
程式設計並不容易,我不會輕忽程式設計師每天面臨的挑戰。但這並不表示內部可重新編程性應該被貶低到 1970 年代的未來願景。現代 Dynabook 缺乏 Kay 願景中的內部可重新編程性,很大一部分原因是它尚未被視為足夠高的優先順序。也許我們應該更多思考這件事。
修訂
2020-02-24:更新以提及 Unix 命令列並移除一些過時的資料