LWN:Elisp里另一種模式匹配條件執(zhí)行方式!
關注了就能看到更多這么棒的文章哦~
An alternate pattern-matching conditional for Elisp
By Jake Edge
March 1, 2024
Gemini translation
https://lwn.net/Articles/961682/
我們在十一月份看到過 的關于在 Emacs Lisp(Elisp)中使用 Common Lisp 特性的(非常)長的討論,帶來的結果之一就是開始了把 Common Lisp 從 Emacs 中移除掉。Richard Stallman 開始了對使用 Common Lisp 庫(cl-lib)的 Emacs 中 Elisp 的一些重寫,以減少維護 Emacs 本身所需的認知負荷。自那時起,他努力簡化 Elisp 的工作,通過添加一個新的模式匹配條件,希望替換長期以來他認為過于復雜的pcase。
復雜之處
在十一月中旬,Stallman指出,他發(fā)現 pcase 定義的他稱為“l(fā)ittle language”的邏輯“簡潔到讓人難以理解”。他認為解決相同的一組問題,采用的把更簡單的 Elisp 構造,比如cond和let 結合起來的做法,是“冗長且麻煩的”,但 pcase 已經將對簡潔性的渴望推到了一個不可取的極端。他說,這給所有不得不維護使用 pcase 的 Emacs 開發(fā)人員帶來了成本,因此他決定將一些 pcase 特性調整到其他構造(constructs)中。
可預見地,這引發(fā)了一系列長篇討論(emacs-devel 郵件列表的常見情況)討論是否需要 pcase 的替代方案,替代方案可能是什么樣子,等等。Stallman新開了一個子線程來研究他對一個新宏 cond* 的想法,它將提供一個更簡單的模式匹配構造,仍然比使用“老式的 Lisp”更簡潔。他的新宏旨在結合條件 cond 形式,它處理檢查多個不同值–類似于其他語言的(switch構造)與 let ,它在受限范圍內臨時把值綁定到變量。 pcase 和 cond* 都旨在為 Elisp 提供一個ML樣式的模式匹配條件機制。
一個簡單的 pcase 示例可以展示出這些構造的一般風格,但兩者能做的功能要更多,包括模式匹配和解開列表(pulling lists)和其他數據結構,這就是“解構(destructuring)”。這個例子,摘自 pcase 文檔,它處理了多種不同類型(例如字符串,符號)的返回代碼,為每個生成了一個適當的消息:
(pcase (get-return-code x)
;; string
((and (pred stringp) msg)
(message "%s" msg))
;; symbol
('success (message "Done!"))
('would-block (message "Sorry, can't do it now"))
('read-only (message "The schmilblick is read-only"))
('access-denied (message "You do not have the needed rights"))
;; default
(code (message "Unknown return code %S" code)))
Stallman 將他的方法描述為試圖“避免 pcase 的應有盡有所導致的笨拙”。自然地,這引發(fā)了對 pcase 的進一步爭論。例如,Michael Heerdegen說:“在我看來‘pcase’非常接近完成這個任務的最佳解決方案。”
到了十二月中旬,Stallman詢問了 cond* 所需的特性“以便很少遇到無法干凈地取代 pcase 的情況”。這個對話發(fā)生在 pcase 替換討論的另一個分支中,這使得它有點難以跟進。在這個線程中,其他人正在與 Stallman 一起調整 cond* ;同樣,其中有很多有幫助的建議和澄清疑問,還夾雜著一些抱怨。然而,Stallman 顯然正在推進這個功能。
在十一月中旬,Alan Mackenzie 引發(fā)了一個問題,似乎自 2010年添加 pcase 以來就一直存在的問題:文檔。他指向了他在 2015 年發(fā)表的一個帖子,描述了 pcase 文檔存在的問題;當時已經解決了其中很多問題,但還有一個正在進行的工作,由 Jim Porter 領導來改進這個宏的文檔。
Porter 列舉了需要關注的多個方面,包括將用于模式匹配和解構的反引號("`")運算符的呈現提前放在 pcase 文檔字符串(doc string)中。正如 Mackenzie 所指出的, pcase 令人困惑的一點是它使用了兩個標點符號,即反引號和逗號(","),這兩個標點符號在 Elisp 宏定義中已經有了確定的用法:“在 pcase 之前,這兩者有明確的含義。但是此后它們變得高度依賴上下文?!?br>
對于 Porter 的帖子有一些回應,大多數都贊成他的觀點;最終,Emacs 共同維護者 Stefan Kangas將該帖子復制給了前 Emacs 維護者、 pcase 的開發(fā)者 Stefan Monnier。Monnier也基本上同意;他認為文檔字符串實際上并不是詳細介紹反引號信息的地方,盡管進行一些語言上的重新調整是有道理的。Stallman對Porter的其他建議提出了異議,可能在“《[在 Emacs Lisp 中進行編程的介紹》](https://www.gnu.org/software/emacs/manual/html_node/eintr/index.html)”中提到 pcase 。"我們不應該鼓勵人們學習 Emacs Lisp 來使用 pcase 。""
初稿
一月中旬,Stallman發(fā)布了cond*的初稿,要求人們進行更多的測試,期待“富有建設性的意見、錯誤報告、補丁和建議”。Andrea Corallo詢問了關于 cond* 的一個特定特性:
一些 cond* 子句為什么要讓變量綁定在這個子句之外(在整個
cond*結構范圍內)的原因是什么?乍一看對我來說在 Lisp 術語中這種做法并不是很常見。
Corallo 指的是 bind* 子句,它可以出現在 cond* 的主體中的任何地方,以用于創(chuàng)建變量的綁定,其作用域并不在包含它們的 bind* 結束的位置。而是從那一點開始生效,這些變量的作用域就是 cond* 的主體,這與 Lisp 的通常期望有些不一樣。
(cond*
(CONDITION FORM)
((bind* (x 42))) ; create a binding of 42 to x
(CONDITION-using-x FORM-using-x)
...)
正如 Távora指出的,其他人已經詢問過這種行為,但他“不確定最終是否澄清了”。他還想知道 cond* 是否可以使用 pcase 來構建;如果它是 pcase 功能的一個嚴格子集,可能有助于這樣做。這“實際上可能會為‘cond*’的采用路徑(盡管這條路徑對于某些人來說仍然是有疑問的)提供便利”。但是 Stallman并不這樣看待;他列舉了他認為 cond* 的優(yōu)勢,并表示他打算將這個新的宏添加到 Emacs 中:
與
pcase相比,cond*具有四個基本優(yōu)勢:可以使綁定覆蓋整個主體、匹配各種數據對象的模式(不一定是相同的)、在子句中使用普通的 Lisp 表達式作為條件、以及[能夠]進行綁定并繼續(xù)使用進一步的子句。我將進行更多測試,然后讓 cond* 功能上線。
Adam Porter要求Stallman 重新考慮將 cond* 安裝到 Emacs 中的做法,建議他應該考慮增強 pcase 而不是添加一整個新的基礎設施讓開發(fā)人員來學習。 pcase 的抱怨之一是學習的負擔;“需要學習 pcase 和 cond* 這兩者了,這無助于減輕人們的學習成本?!盇dam Porter 指出 pcase 可以處理 Stallman 所列舉的許多優(yōu)勢,因此可能有一條增強 pcase 的路徑:
您編寫 cond* 的陳述理由包括了 pcase 的一些局限性。其中一些,例如文檔,已經有志愿者站出來解決。其他的問題也可以通過各種方式解決。我已經提出了一些建議,但您沒有解釋拒絕它們的原因。
不出所料,Stallman不同意:
如果 pcase 對于某些特定任務缺少功能,通過添加一些功能來解決問題將會很容易。然而,pcase 的問題在于它對于它所做的工作來說具有了太多的功能。cond* 可以用更少的功能做同樣的工作,因為它們更好地協同工作。
ELPA?
Kangas 表示他并未認真關注討論,但對“計劃安裝'cond*' 的做法”感到驚訝,“否則我會更早地發(fā)表意見”。添加 cond* 將導致 Emacs 的維護工作變得更加困難,因為 pcase 并沒有被淘汰,因此將會有“不止一個相對復雜的宏,而是變成了兩個宏”需要他了解和理解。如果 cond* 帶來了實質性的好處,那么這樣做可能值得,但他并不認同;“我所看到的只是'pcase'的一種版本?!币虼?,他建議為GNU Emacs Lisp Package Archive(ELPA)創(chuàng)建一個新包,這將提供“探索現有宏的替代版本的良好途徑”。
Kangas 認為沒有全面替換 pcase 的計劃是一個好事。這樣避免了自然而然會導致的大量代碼變動和錯誤。但 Mackenzie持相反意見;他認為完全替換 pcase 將是“一舉改善我們代碼的可讀性和可維護性”的良好目標。他認為將其放入 ELPA 庫中“意味著它永遠不會有所進展,只會被遺忘”;而在 Emacs 主分支中開始功能分支并最終合并將是更好的做法。
與其他幾人一樣,Monnier認為 cond* 只是又進一步復雜化了 Elisp。另一方面,他說,對他來說反對它有點虛偽:
所以,我并不是非常熱衷于添加這樣一個新形式,但我作為多年來引入 ELisp 中幾種這樣的新構造的責任人,我感覺有點像一個褒貶不一的人。
所以,與其反對它,我更愿意指出一些我認為可以改進的事情(或令我不快的事情)。
隨后 Stallman 和 Monnier 就 cond* 以及它與 pcase 的重疊(以及兩者可能如何“達成妥協”)進行了一連串的討論,以及關于傳統(tǒng) cond 形式在綁定條件內部存在的一些不足的討論等。討論的大部分圍繞著 Monnier 的擔憂展開,他認為兩種構造有重疊但仍有不同的模式語言:
[…]我看到一個相當豐富但硬編碼的模式語言,與 Pcase 相比,這似乎是一個倒退,Pcase 的模式語言只是由幾個基本原語組成的(其他部分是通過'pcase-defmacro'定義的)。最糟糕的是,兩種模式語言非常相似,但我看不出有任何好的理由讓它們不同。
Monnier強調Stallman 可以直接重用 pcase 的低級模式語言用于 cond* ,這將帶來許多好處:“工作量更小,代碼重復更少,文檔重復更少,對于程序員學習的內容也更少。而且很可能您會隨后完善 Pcase,這樣每個人都會受益。”
截至目前仍在進行討論,尚不清楚 Stallman 將如何處理模式語言的問題。Monnier稱,通過相應的補丁可以很容易將 match* 形式更改為使用 pcase 機制;相關的補丁已經被發(fā)送。Mackenzie反對 cond* 使用 pcase 的模式處理方式,部分原因是因為缺乏對低級 pcase 機制的文檔說明。Alfred M. Szmidt希望最終 pcase 可以被編寫成使用 cond* ,而不是相反,但 Monnier表示這在技術上是不可行的。
添加 cond*
一月底,Emacs 的共同維護者 Eli Zaretskii 和 Kangas宣布 cond* 將被安裝到 Emacs 核心中,作為 pcase 的替代方案;然而,并沒有放棄 pcase ,因為它被認為是一種“風格偏好問題”。在帖子中,Kangas 明確表示政治上而不是嚴格的技術考慮是決策過程的一部分:
作為維護者,我們的首要責任是確保我們都能夠共同努力,團結在同一個旗幟之下。我們項目的成功取決于此。因此,我們最不希望做的事情是排斥任何一組貢獻者,無論人數多少。
我們相信這比對 cond* 或 pcase 的論證更重要。簡單的事實是我們擁有不同的背景和經驗,這些經歷往往使我們位于這個討論的兩邊。這種多樣性是一種力量,而不是弱點。
盡管如此,顯然仍然有一些人對 14 年前 pcase 被安裝到 Emacs 中感到不滿,至少對于 Mackenzie 來說,是這樣的。他認為維護者選擇的中庸之道不會有助于解決他和其他人在理解一些 pcase 使用方面遇到的問題;他仍然主張使用 cond* 進行全面替換。另一方面,Stallman認為不應該替換=pcase=,但“希望避免在 Emacs 中使用 pcase,應該支持 cond*”。當然,這一決定并沒有阻止另一場關于“ cond* 與 pcase ”的討論持續(xù)展開,自然也一直在進行中。
一月底,Stallman表示他幾乎準備好將代碼提交到Emacs Git存儲庫的代碼,盡管截至目前尚未提交。Zaretskii要求同時將文檔添加到Elisp參考手冊,因此這可能會拖慢進程。不過過不了多久, cond* 應該會出現在 Emacs 中,因此將會有兩種方式來進行模式匹配和解構能力的代碼實現——無論這是好事還是壞事。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協議。
長按下面二維碼關注,關注 LWN 深度文章以及開源社區(qū)的各種新近言論~
