二十年 Pandoc:Haskell 文件轉換器的歷史
介紹 – 為什麼 Pandoc 重要
Pandoc 的 20 週年紀念展示了如何一個小型、自行編寫的 Markdown 解析器演變成目前最受歡迎的 Haskell 程式,現在支援 51 種輸入與 76 種輸出格式,並安裝在數百萬台機器上。其長壽命展示了乾淨的 AST‑based 架構、強型別以及社群驅動開發模式的力量。
前史:先選 Haskell,再考慮文件轉換
John MacFarlane 在決定寫文件轉換器之前就選擇了 Haskell。在閱讀《A Gentle Introduction to Haskell》並嘗試使用 parsec 解析子庫後,他構建了一個產生真正抽象語法樹(AST)而非基於正則表達式的 HTML 輸出的 Markdown 解析器。此設計使得能夠實現 N × M 轉換模型:每新增一個讀取器(解析器)和寫入器(渲染器)都會使可能的格式對數相乘。
"我決定使用 Haskell,然後決定用它來寫文件轉換器。" – John MacFarlane
早期程式碼基礎(約 3 k 行)支援 Markdown、reStructuredText、HTML、LaTeX、RTF 和 S5,且僅需 GHC 的標準函式庫。
首次發行(2006‑2008):社群驅動的可見度
- 0.1 (Aug 2006) – 原始碼發表在作者的網站上;除了兩封好友的電子郵件外,沒有其他宣傳。
- Debian 打包 (Oct 2006) – 與 Recai Oktaş 合作提升曝光度。
- 0.3 (2007) – 加入 DocBook 寫入器和腳註語法。
- 0.4 (2007) – 引入表格、定義清單、上標以及首次 Hackage 發布,透過
cabal-install實現依賴管理。
這些版本幾乎完全依照作者個人工作流程進行,但每一次新增都開闢了新的使用案例,吸引了更多使用者。
Pandoc 1.x (2008‑2017):擴充格式生態系統
- 1.0 (Sep 2008) – 加入 MediaWiki、GNU Texinfo、OpenDocument、ODT、围栏程式碼區塊以及語法高亮。為支援 ODT 和高亮,MacFarlane 編寫了
zip-archive和highlighting-kate庫。 - 1.4 (Jan 2010) – 引入靈活的模板系統,以實現可自訂的輸出。
- GitHub 遷移 (2010) – 從 Google Code 遷移至 GitHub,顯著提升可見度。
- 1.9 (2012) – 啟用 Word .docx 輸出、AsciiDoc 寫入器,並支援 Beamer/DZSlides。
- 2013‑2014 – 加入 Markdown 擴充功能、YAML 中繼資料區塊、Lua 自訂寫入器、JSON 過濾器,以及外部
pandoc-citeproc過濾器用於引用。 - CommonMark 整合 (2015‑2020) – 透過
libcmark加入 CommonMark 解析器,之後改用原生 Haskell 套件,同時保留 Pandoc 的擴充 Markdown 方言。
在此時期,貢獻者如 Albert Krewinkel、Jesse Rosenthal 和 Matthew Pickering 提供了主要格式支援(Org‑mode、docx 讀取器、EPUB 等)。
Pandoc 2.x (2017‑2023):架構大改與效能提升
- 2.0 (2017) – 引入
PandocMonadtypeclass,允許純粹與具 I/O 能力的讀取器/寫入器共存,並加入 Lua 過濾器以進行快速 AST 操作。 - Lua 引擎 – 建基於
hslua,使使用者能編寫 Lua 過濾器與自訂寫入器。 - Skylighting – 用更快、更精準的語法高亮函式庫取代
highlighting-kate。 - 引用大改 (2.11) – 用原生 Haskell CSL 庫取代外部
pandoc-citeproc過濾器,並加入純 Haskell Unicode 排序實作。 - 沙箱模式 (2.15) – 保證無副作用執行,利用先前的
PandocMonad設計。 - 預設檔案 (2.8) – 允許可重複使用的選項集合。
- 網頁伺服器 (2.19.1) – 將 Pandoc 公開為 HTTP API。
Handshake 在 2018 年的 10 萬美元捐助資助了核心維護者的 stipends,維持了快速的開發節奏。
Pandoc 3.x (2023‑現在):模組化與新前端
- 3.0 (2023) – 將專案拆分為四個套件:
pandoc(核心庫)、pandoc-lua-engine、pandoc-server和pandoc-cli。CLI 現在可以在不含 Lua 或伺服器元件的情況下編譯,以減少二進位檔案大小,適合輕量使用。 - Figure 元素與分塊 HTML 寫入器 – 新增原生支援多章節 HTML 書籍。
- Typst 整合 (3.1.3) – 透過
typstHackage 套件建立完整功能的 Typst 讀取器。 - Djot 支援 (3.1.12) – 為作者設計的 Markdown 後繼語言 Djot 新增輸入與輸出支援。
- WASM 編譯 (3.9, Feb 2026) – 使 Pandoc 能在瀏覽器中完整執行,並附帶名為「pandoc for the people」的 GUI 前端。
2024‑2025 年的社群貢獻新增了 ANSI、mdoc、POD、XML AST、vimdoc、PowerPoint、Excel、BBCode 以及 AsciiDoc 讀取器/寫入器,說明核心架構的持續擴充性。
影響力的統計數據
- 支援格式 – 51 輸入 × 76 輸出 = 3 876 種不同的轉換(不計擴充變體)。
- 程式碼大小 – 核心套件約含 85 k 行 Haskell 程式碼;連同相依套件總行數大約翻倍。
- GitHub 活動 – 7 346 個已關閉議題;超過 600 位貢獻者。
- 主要貢獻者 – John MacFarlane (372 317 行變更)、Albert Krewinkel (77 136)、Jesse Rosenthal (39 664) 等。
- 安裝規模 – Pandoc 已安裝在數百萬台電腦上,並整合至 Quarto、Jupyter Notebook 以及許多 CI 流程。
為何 Haskell 是決定性因素
作者認為 Haskell 的代數資料型別、強靜態型別與純粹性使得大規模重構變得安全且具表達力。純函式保證沙箱模式能強制執行「無 I/O」語義,而型別系統在編譯時期就能捕捉不匹配,降低回歸風險。
"當使用缺乏強型別系統的語言時,例如 Python 和 JavaScript,缺乏這些保障總是讓我對進行大規模變更感到害怕。"
雖然承認 Rust 在效能上的優勢,作者仍認為 Haskell 在表達複雜文件轉換時更具人體工學。
社群反思(精選 HN 評論)
- 長壽與效率 – 一位評論者指出,即使大型語言模型(LLM)趨於可靠決定性,Pandoc 在批次轉換方面仍比 LLM 高效幾個數量級,能源消耗更低。
- 貢獻者體驗 – 使用者讚揚維護者的友善,指出對錯誤回報和 PR 的回應快速且親切,即使來自不熟悉 Haskell 的開發者也是如此。
- 實際工作流程 – 多位使用者分享了使用 Pandoc 進行電子郵件轉 Markdown 管線、靜態站點生成,以及對二進位文件進行 Git diff 正規化的程式碼片段。
- 未來展望 – 有部分人擔心 AI 驅動的格式翻譯最終可能取代規則型轉換器,但多數人同意 Pandoc 的確定性輸出與低資源佔用使其今日仍具相關性。
展望:Pandoc 能否在 AI 時代倖存?
作者推測大型語言模型已能在標記語言之間進行翻譯,但 Pandoc 仍提供三項具體優勢:
- 生態足跡 – 與運行 LLM 相比,能源消耗遠低。
- 確定性結果 – 相同輸入永遠產生相同輸出,這對可重現的科學工作流程至關重要。
- 成熟度與可靠性 – 數十年的錯誤修復與社群測試產出穩定工具,AI 模型尚未達到此水準。
不過,作者承認未來的 AI 終有可能在處理含糊標記時超越規則型轉換。
結論
Pandoc 從 3 k 行的興趣專案發展至具備 WASM 能力的模組化生態系統,二十年來的歷程凸顯了良好設計的 AST、強型別以及開放的貢獻文化如何讓複雜軟體工具得以長期維持。其持續相關性建基於效率、確定性輸出以及一個活躍的社群,該社群不斷新增格式、過濾器與整合。