Emacs 作為服務編排器:使用 Elisp 構建客戶端

Emacs 作為服務編排器:使用 Elisp 構建客戶端

Emacs 作為服務編排器

Emacs 並非作業系統,但它能作為高階編排器,在作業系統核心層級之上管理應用程式與工具。透過利用內建對系統服務的存取能力以及 Emacs Lisp (Elisp) 的靈活性,使用者可以將幾乎任何外部資源——無論是網路 API 還是本地命令列工具——視為可供自定義客戶端使用的服務。

Emacs 中的客戶端-伺服器模型

在標準的客戶端-伺服器模型中,任務被拆分為服務(資源提供者)與客戶端(請求者)。Emacs 在實現此互動的客戶端端點方面具有獨特的地位,因為它為客戶端的三个主要關注點提供了原生函式庫:

1. 使用者介面 (UI)

Emacs 提供多種內建 UI 組件來顯示數據並擷取輸入,包括:

  • Minibuffers and Buffers: 用於基本的文字互動與數據顯示。
  • Completion and Transient: 用於高效的使用者輸入與指令導航。
  • Tabulated List Mode and vtable: 用於結構化數據呈現。

2. 客戶端邊緣 (通訊)

Emacs 提供必要的管道來透過以下方式與外部服務通訊:

  • Network Protocols: 內建對 URL (HTTP/HTTPS), Sockets (TCP/UDP), 以及 SMTP 的支援。
  • Data Serialization: 用於解析與生成 JSON 和 XML 的原生函式庫。

3. 本地資料庫 (數據管理)

為了管理交換數據的狀態,Emacs 利用:

  • Collections: 關聯列表 (Association lists)、屬性列表 (Property lists) 以及雜湊表 (Hash tables)。
  • Persistent Storage: 內建 SQLite 支援,以滿足更複雜的數據需求。

實作服務客戶端:wttr.in 範例

由於 Elisp 是動態語言,開發者可以快速進行客戶端原型設計。例如,一個針對 wttr.in 的天氣客戶端可以透過以下步驟,在約 67 行程式碼中實現:

  1. Request Construction: 使用 url-generic-parse-url 來構建對 wttr.in JSON API 的請求。
  2. Data Retrieval: 使用 url-retrieve-synchronously 來獲取數據,並使用 json-parse-buffer 將回應轉換為 Elisp 雜湊表。
  3. Data Extraction: 解析雜湊表以提取特定值(例如溫度與天氣描述),並將其格式化為人類可讀的字串。
  4. Display: 將最終字串輸出到 minibuffer 並存入 kill-ring 以供日後使用。

將 CLI 工具重新定義為服務

當不需要專用的 Elisp 函式庫時,任何命令列工具都可以被重新定義為「服務」。透過使用 shell-command-to-string,Emacs 可以向本地腳本(例如處理 API 呼叫的 Python 腳本)發送請求,並將該腳本的輸出視為服務回應。這允許在不編寫大量樣板程式碼的情況下,高度即興地整合外部工具。

社群觀點與技術背景

雖然「萬物皆服務」的方法非常強大,但社群對 Emacs 的本質提供了幾種細微的觀點:

  • Lisp Machine Heritage: 一些使用者認為 Emacs 不僅僅是一個 OS,更像是 Lisp Machine 概念的實現——一個內建文字編輯器的程式設計環境,而非反之。
  • Comparison to Modern Standards: 雖然 Language Server Protocol (LSP) 已將編輯器中的許多客戶端-伺服器互動標準化,但貢獻者指出,Emacs 在 LSP 出現之前,就已經擁有長期運行的子程序、類 RPC 互動以及 TRAMP (Transparent Remote Access, Multiple Protocols)。
  • The "Blob" Architecture: 一些人將 Emacs 描述為「一團不分彼此的 Lisp」,其中核心系統與插件之間的界限變得模糊,允許無縫地整合新服務。

"Emacs 更像是一個內建文字編輯器的程式設計環境。有點像某些 SmallTalk 映像檔。"

"Emacs 在 LSP 出現之前,就已經擁有長期運行的子程序、類 RPC 互動、TRAMP、GUD、REPL 整合等。LSP 標準化了一個重要的介面,但它並非架構的起點。"

Sources