二十年Pandoc:Haskell文档转换器的历史

引言 – 为什么Pandoc重要

Pandoc的20周年展示了一个小型、自编写的Markdown解析器如何发展成为最受欢迎的Haskell程序,现在支持51种输入格式和76种输出格式,并在数百万台机器上安装。其长寿命展示了基于干净AST的架构、强类型和社区驱动开发模型的力量。


预史:首先是Haskell,其次是文档转换

John MacFarlane在决定编写文档转换器之前就选择了Haskell。阅读《Haskell温和介绍》并尝试使用parsec解析器组合子库后,他构建了一个生成真实抽象语法树(AST)而不是基于正则表达式的HTML输出的Markdown解析器。这种设计使得N × M转换模型成为可能:每新增一个读取器(解析器)和写入器(渲染器)都会使可能的格式对数量相乘。

"我决定使用Haskell,然后决定用它编写一个文档转换器。" – John MacFarlane

早期代码库(约3 k行)支持Markdown、reStructuredText、HTML、LaTeX、RTF和S5,仅需GHC的标准库。


首次发布(2006‑2008):社区驱动的可见性

  • 0.1(2006年8月) – 源代码发布在作者的网站上;除了两封朋友的电子邮件外,没有其他宣传。
  • Debian打包(2006年10月) – 与Recai Oktaş合作增加了曝光度。
  • 0.3(2007) – 添加了DocBook写入器和脚注语法。
  • 0.4(2007) – 引入了表格、定义列表、上标以及第一个Hackage发布,通过cabal-install实现依赖管理。

这些发布几乎完全由作者的个人工作流程指导,但每次添加都打开了新的用例,吸引了更多用户。


Pandoc 1.x(2008‑2017):扩展格式生态系统

  • 1.0(2008年9月) – 添加了MediaWiki、GNU Texinfo、OpenDocument、ODT、围栏代码块和语法高亮。为了支持ODT和高亮,MacFarlane编写了zip-archivehighlighting-kate库。
  • 1.4(2010年1月) – 引入了灵活的模板系统以实现可定制的输出。
  • GitHub迁移(2010年) – 从Google Code迁移,显著提高可见度。
  • 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) – 引入了PandocMonad类型类,允许纯函数和具备I/O能力的读取器/写入器,并添加了用于快速AST操作的Lua过滤器。
  • Lua引擎 – 基于hslua构建,使用户能够编写Lua过滤器和自定义写入器。
  • Skylighting – 用更快、更准确的语法高亮库替换了highlighting-kate
  • 引用大修(2.11) – 用原生Haskell CSL库和纯Haskell Unicode排序实现替换了外部的pandoc-citeproc过滤器。
  • 沙盒模式(2.15) – 保证无副作用执行,利用早期的PandocMonad设计。
  • 默认文件(2.8) – 允许可重用的选项集合。
  • Web服务器(2.19.1) – 将Pandoc作为HTTP API公开。

Handshake(2018年)的10万美元捐助为核心维护者提供了津贴,维持了快速的开发速度。


Pandoc 3.x(2023‑至今):模块化和新前端

  • 3.0(2023年) – 将项目拆分为四个包:pandoc(核心库)、pandoc-lua-enginepandoc-serverpandoc-cli。CLI现在可以在不包含Lua或服务器组件的情况下构建,从而减小二进制大小以适用于轻量级使用。
  • 图元素和分块HTML写入器 – 添加了对多章HTML书籍的原生支持。
  • Typst集成(3.1.3) – 通过Hackage上的typst包创建了功能完整的Typst读取器。
  • Djot支持(3.1.12) – 为作者设计的Markdown继承者Djot标记语言添加了输入和输出支持。
  • WASM编译(3.9,2026年2月) – 使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评论)

  • 寿命与效率 – 一位评论者指出,即使LLMs接近确定性可靠性,Pandoc在批量转换方面仍然比LLM节能几个数量级。
  • 贡献者体验 – 用户称赞维护者的友好,引用对错误报告和PR的快速、友善回应,即使来自不熟悉Haskell的开发者。
  • 实际工作流程 – 几位用户分享了使用Pandoc进行电子邮件到Markdown管道、静态站点生成以及Git diff规范化二进制文档的片段。
  • 未来展望 – 一些人表达了对AI驱动的格式翻译最终可能取代基于规则的转换器的担忧,但大多数人同意Pandoc的确定性输出和低资源使用使其今天仍然相关。

展望未来:Pandoc能否在AI时代幸存?

作者推测,大型语言模型已经能够在标记语言之间进行翻译,但Pandoc仍然提供三个具体优势:

  1. 生态足迹 – 相比运行LLM,能耗远低。
  2. 确定性结果 – 相同输入始终产生相同输出,这对可重复的科学工作流程至关重要。
  3. 成熟度和可靠性 – 十余年的错误修复和社区测试产生了一个稳定的工具,而AI模型尚未达到这一水平。

不过,作者承认未来的AI可能最终在特别是对歧义标记情况时超越基于规则的转换。


结论

Pandoc的20年历程——从一个3 k行的爱好项目发展为具备WASM能力的模块化生态系统——说明了一个设计良好的AST、强类型和开放的贡献者文化如何使一个复杂的软件工具得以持续数十年。其持续的相关性建立在效率、确定性输出以及一个活跃的社区之上,该社区不断添加格式、过滤器和集成。

Sources