使用 Emacs 搭配 SLIME 寫 Common Lisp 程式

    前言

    SLIME 本身是 Emacs 的模式 (mode),其目的是輔助撰寫 Common Lisp 程式。

    雖然現在使用 Emacs 的程式設計者逐漸變少,SLIME 在 Common Lisp 開發工具中算是整合得不錯的。許多 Common Lisp 的教學資源還是會提到 SLIME,所以筆者特地寫了一篇文章來介紹這個開發工具。

    如果讀者不需要 REPL 環境,不一定要用 SLIME,也可以用 VSCode 等編譯器來寫 Common Lisp 程式。當我們用 VSCode 寫 Common Lisp 程式時,把 Common Lisp 當成 Python 或 Ruby 等直譯語言來用,不注重 REPL 環境。

    為什麼不用 Emacs

    如果讀者在學 Common Lisp 前完全沒用過 Emacs,最好慎重考慮要不要入這個坑。Emacs 有幾個顯而易見的缺點:

    • 熱鍵多到爆炸
    • 設定檔不易撰寫
    • 誤以為 Emacs 是 IDE
    • 遠端主機幾乎不會預裝 Emacs

    因為 Emacs (註) 本身其實是一個大型的 Lisp 程式,該編輯器使用了自己的 Lisp 方言,即 Emacs Lisp。除了底層用 C 來實作以外,其他的部分都是以 Emacs Lisp 來實作。所以,當我們在寫 Emacs 設定檔時,就是在寫 Emacs Lisp 程式。

    (註) 指 GNU Emacs。

    很多 Emacs 學習資源會讓 Emacs 初學者以為 Emacs 是萬能的,浪費許多時間來調校,結果只是重造其他編輯器或 IDE 的輪子。其實 Emacs 本質上仍然是一個編輯器,如果需要一些和寫程式相關的功能,還是針對各個語言找專門的 IDE 比較好。

    此外,由於 Emacs 比較肥大,遠端主機通常不會預裝 Emacs。如果想要學習可在命令列環境使用的編輯器,還是去學 Vim 或 nano(1) 比較實在。

    為什麼使用 Emacs

    雖然筆者嘴了一下 Emacs 的缺點,但對學習 Common Lisp 來說,Emacs 仍然是值得考慮的工具。因為:

    • SLIME 整合得很好
    • 學會 Common Lisp 自然會設置 Emacs

    雖然 Common Lisp 和 Emacs Lisp 是不相容的,但學習了其中一種 Lisp 方言後,要轉換到另一個 Lisp 方言不會太困難。用 Emacs 學 Common Lisp 可說是相輔相成。

    如果讀者懶得自己調校 Emacs,可以考慮使用 Portacle。該軟體整合了以下軟體:

    • Emacs
    • SBCL (Steel Bank Common Lisp)
    • SLIME
    • Company:自動補完
    • QuickLisp:套件管理軟體
    • Git:知名的版本控制軟體

    本文仍會自行設置 SLIME。對 Portacle 有興趣的讀者可以自己玩玩看。

    使用 SLIME 的前置知識

    Emacs 是需要花一點時間學習才能使用的軟體,但本文的篇幅不足以介紹 Emacs 的使用方式。如果想要學一下 Emacs 的用法,可以參考 Emacs 101

    雖然寫 Emacs 101 的大大已經跳槽到 VSCode 了,這件事無損讀者學習 Emacs。

    Emacs 設定檔

    通常是 $HOME/.emacs$HOME/.emacs.d/init.el 。在使用 Emacs 的過程中,自然會在 $HOME/.emacs.d 安裝一些套件,所以建議使用後者為設定檔。

    Windows 系統中等效家目錄的位置是 %USERPROFILE% 變數所在的位置。假定使用者為 user ,家目錄即位於 C:\Users\user 。舊版的 Windows 系統的家目錄會有空格,最好另行將沒有空白、有讀寫權限的目錄設置至 HOME 變數。

    安裝 SLIME

    使用 MELPA 安裝 SLIME

    新版的 Emacs 已經內建 MELPA 了,所以就不用自己安裝套件管理程式,但還是要自己設置一下。在 Emacs 設定檔中加入以下設定:

    ; Emacs Lisp
    (require 'package)
    (package-initialize)
    (add-to-list 'package-archives
                 '("melpa" . "https://melpa.org/packages/") t)
    (add-to-list 'package-archives
                 '("melpa-stable" . "http://stable.melpa.org/packages/") t)
    

    M-x package-refresh-contents 指令來更新套件清單。

    然後再用 M-x package-install RET M-x slime 指令來安裝 SLIME。

    根據自己使用的 Common Lisp 實作品來指定路徑。以下範例設置是以 64 位元 Windows 的 Clozure CL 為準:

    ; Emacs Lisp
    (setq inferior-lisp-program "C:/Users/user/Documents/ccl/wx86cl64.exe")
    

    如果讀者使用不同系統或不同 Common Lisp 實作品,請自行修改路經。

    手動安裝 SLIME

    手動安裝 SLIME 的方式其實不會太麻煩,用 Git 將 SLIME 的 repo 拷貝 (clone) 下來後,在 Emacs 設定檔中指定 SLIME 的路經即可。參考以下設置:

    ; Emacs Lisp
    (add-to-list 'load-path "C:/Users/user/Documents/slime")
    (require 'slime-autoloads)
    (setq inferior-lisp-program "C:/Users/user/Documents/ccl/wx86cl64.exe")
    

    請不要將此範例設置直接複製貼上,而要根據自己系統實際的情形來修改。

    第一次使用 SLIME

    使用以下指令來啟動 Emacs 並開啟 source.lisp

    $ emacs source.lisp
    

    source.lisp 不存在,會自動建立一個同名的空白檔案。所以,不論 source.lisp 是否存在,該行指令皆可順利執行。

    在預設情境下,Emacs 會以圖形介面模式開啟。但 Emacs 原本的設計是在命令列環境下操作。若想在命令列模式中使用 Emacs,可加入 -nw 參數:

    $ emacs -nw source.lisp
    

    這時候,不論 Emacs 是否有圖形介面,皆會以命令列模式來開啟。

    進入編輯畫面後,輸入 M-x slime 來啟動 REPL 環境。為什麼不在開啟 Lisp 檔案時自動啟動 REPL 環境呢?因為同一個軟體專案很有可能有多個 Lisp 原始碼,每開一個 Lisp 原始碼就觸發一次 REPL 環境反而是不良的行為。

    開啟 REPL 環境後,Emacs 會分割成上下兩個緩衝區 (buffer):

    使用 Emacs 的 SLIME 模式

    註:筆者在 macOS 中以命令列模式執行 Emacs,使用 SLIME 模式編輯 Common Lisp 程式。

    使用 C-x o 指令即可在多個緩衝區間切換。透過 SLIME 的整合,我們可以在 Emacs 內同時使用編輯器和 REPL 環境。

    編輯完成後,記得用 C-x C-s 存檔。

    使用 C-c C-k 指令即可在不離開 Emacs 的前提下編譯並執行 Lisp 程式碼。

    查詢符號 (Symbol) 和函式 (Function) 的用法

    除了編譯和執行程式外,SLIME 支援即時 API 查詢。將游標移到特定的符號或函式後,使用 C-c C-d h 指令即可自動連線到 HyperSpec 中相對應的頁面。算是相當方便的功能。

    使用 SLIME 的注意事項

    SLIME 將 Common Lisp 當成後端 (backend) 程式來使用。在 Common Lisp 中使用 quit 指令會導致 SLIME 和 Common Lisp 間的連線中斷。所以,在使用 SLIME 時,要停掉 quit 指令。

    參考以下短例:

    ;; Custom-made main function.
    (defun main ()
      (write-line "Hello World")
      ;; Disable `quit` command in SLIME session.
      #-swank (quit)
      )
    

    SLIME 利用 swank 伺服程式和 Common Lisp 後端連線。所以,此範例程式在 swank session 中選擇性註解掉 quit 指令。程式就不會從 SLIME 的 REPL 環境中斷掉。

    【分享本文】
    Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Yahoo
    【追蹤本站】
    Facebook Facebook Twitter Parler