简介:这是一款面向Agda程序员的VS Code扩展,将Emacs下的agda-mode核心交互迁移到现代编辑器,解决依赖类型编程与形式化验证场景中缺乏顺手编辑工具的问题。资源包共179个文件,压缩后457KB,包含67个JS脚本、64个res源码、13个out输入输出示例、4个agda测试文件及配置文档等,JS与res构成扩展主体,agda文件则用于验证命令与语法高亮效果。项目支持通过Ctrl+C Ctrl+L加载文件,提供类型推断、case拆分、输入法支持等关键操作,并内置了Agda语言服务器的实验性接入指引,方便开发者对比Emacs与LSP模式的使用差异。目前已有236人学习下载,适合熟悉Agda基础语法、希望提升交互式证明与编程效率的VS Code使用者。包内目录结构清晰,附带Issue95.agda等边界案例和codicon样式资源,便于扩展开发者研究实现或二次修改。 在写形式化证明这件事上,我折腾过的编辑器不少:Emacs 的 agda-mode 是官方正统,但配置门槛和快捷键学习成本实在不低;后来转战 VS Code,发现它的 agda-mode 扩展做得比我想象中成熟很多。这篇博文就来聊聊如何在 VS Code 里把 Agda 这套交互式证明环境跑起来,从安装、输入法到日常按键、踩坑排查,一次性说清楚。适合刚接触 Agda 的初学者,也适合从 Emacs 迁徙过来的老手。
1. 为什么是 Agda,为什么在 VS Code 上写证明
1.1 Agda 到底是个什么“语言”
Agda 是一门依赖类型的函数式编程语言,但它和普通的编程语言有一个本质区别:你在 Agda 里写的每一个函数、每一条定义,都可能同时是一个数学证明。这里的关键在于 Curry-Howard 同构——程序即证明,类型即命题。比如你写一个类型n : Nat → n + 0 ≡ n,如果能把对应的函数完整写出来且通过类型检查,那就等于完成了这个定理的证明。
这个理念让 Agda 在形式化验证、编程语言理论、类型系统研究等领域有大量应用。很多人第一次看到 Agda 代码里的Set、refl、suc会一头雾水,但只要理解了“类型就是断言、项就是证据”这套思路,看代码的速度会快很多。相比 Coq、Lean 这些同样出名的证明助手,Agda 的语法更接近 Haskell 等纯函数式语言,写起来手感很“编程”,同时它对模式的精细支持也使得证明过程非常直观。
1.2 从 Emacs 迁移到 VS Code 的真实理由
Emacs 上的 agda-mode 是官方主推的环境,功能最全、跟随语言版本也最及时。但问题也很明显:Emacs 的配置对新手不友好,快捷键零基础基本无从下手,而且界面风格放在今天来看确实有点“复古”。VS Code 这边的 agda-mode 扩展把核心交互能力都搬了过来,虽然细节上有些差异,但日常使用完全足够。
我选择在 VS Code 上写 Agda 的理由很简单:编辑体验现代化,悬停显示类型、跳转定义、错误波浪线这些都是原生功能;命令面板可以随时搜索 Agda 操作,不用背快捷键;再加上我本来就用 VS Code 写其他项目,没必要为了 Agda 单独开一个 Emacs。当然,如果你是重度 Emacs 用户,继续用官方 agda-mode 也完全没问题,这套扩展对标的正是它的操作逻辑,两边切换不会太别扭。
2. 环境准备与安装全流程
2.1 先把 Agda 编译器本体装好
VS Code 里的 agda-mode 扩展本质上是一个前端,真正的语法检查、类型推导、hole 交互全都依赖命令行里的agda二进制。所以第一步永远是先把 Agda 装好,并且确保它在系统的 PATH 环境变量里,否则 VS Code 扩展根本无法工作。
Windows 上我建议直接去 Agda 的 GitHub Releases 页面下载官方安装包,装完打开一个终端输入agda --version确认输出版本号。如果你习惯用 Windows 包管理器,choco install agda或scoop install agda也是可以的,但我个人遇到过一次 scoop 安装后 VS Code 无法定位 agda 的情况,最后还是手动装官方包解决了。macOS 上一行brew install agda搞定,Linux 发行版比如 Ubuntu、Arch 也都有现成包,但要注意:发行版仓库里 agda 版本可能偏老,而 VS Code 扩展有时对新版本特性有依赖,如果安装后功能异常,优先考虑从源码编译或从官方仓库获取更新版本。
安装完成后,强烈建议顺手配一下 Agda 的全局库环境。在用户主目录下创建~/.agda文件夹,里面放两个文件:libraries和defaults。如果你要用标准库(写证明几乎离不开),就把标准库的.agda-lib文件路径写到libraries里,然后在defaults里写上一行库名,这样每次启动 Agda 都会自动加载标准库,省去很多-l参数的麻烦。
2.2 VS Code 扩展安装与首个文件加载
在 VS Code 扩展市场搜索agda-mode,装好之后建议顺带安装agda-input这个扩展,它负责提供 Unicode 数学符号的输入法支持。装完记得重载窗口,让扩展生效。打开命令面板(Ctrl+Shift+P),输入Agda: Load,如果你的文件是空的或者还没有保存,扩展会提示你先保存再加载。
让我带你走一遍首个文件的完整流程。先新建一个hello.agda,输入以下内容:
module hello where open import Agda.Builtin.Nat -- 一个非常简单的函数:把自然数加一 suc-test : Nat → Nat suc-test x = suc x保存文件后,按Ctrl+C Ctrl+L(也就是C-c C-l),文件会被加载并进入类型检查。加载成功后,代码里的类型、函数名、关键字会以不同颜色高亮,右下角状态栏会显示 Agda 进程状态。如果你的agda --version正常,但加载时一直报错,大概率是 PATH 配置或版本不匹配问题,这会排在后面第四节专门说。
3. 核心玩法:交互式证明的日常套路
3.1 输入法:怎样打出那些数学符号
Agda 代码里到处是→、λ、≡、∀、×这类 Unicode 符号,这也是劝退很多新手的第一道坎:键盘上根本没有这些键。官方 agda-mode 的解决方案是输入法机制——你输入反斜杠开头的 LaTeX 风格名字,再按空格或 Tab 把它转换成符号。VS Code 的 agda-mode 扩展完整沿用了这套机制。
比如你想输入→,直接打\to然后按空格,它就会自动变成→;输入\lambda按空格得到λ;\times得到×;\bn或\bN得到ℕ这类数学双写字母。具体支持哪些名字,可以输入\后弹出候选列表慢慢翻,或到扩展的文档里查,常用的几十个记住就够用了。如果你想要查找更“人间”的字符,也可以装agda-input扩展后打开它的候选面板搜索,里面按语义分类整理了很多符号。
这里有一个很重要的实操细节:输入法只有在 agda-mode 扩展被激活的文件(即.agda文件)里有效,而且必须在已经加载过的文件中使用——因为在未加载的文件中,符号替换功能不一定生效,你会看到一堆\to没有被转换。如果你发现自己输\to没反应,先执行一次C-c C-l加载文件,再试通常就好了。
3.2 别怕快捷键:核心命令就这几个
VS Code 的 agda-mode 支持一组与 Emacs 版基本一致的快捷键,采用C-c前缀(即Ctrl+C)作为所有 Agda 命令的入口。刚接触的时候容易担心记不住,其实日常高频使用的命令非常有限,我一个一个说。
C-c C-l是加载当前文件,这个最常用,改完代码就要按。C-c C-t是查询类型:把光标放在一个表达式上,按下后下面会显示它的类型,这在写证明时用来“看进度”非常方便。C-c C-c是做 case split(连字符拆分):当你在一个 hole 里按下它,扩展会提示你“split on: 某个变量”,回车后自动把这个变量可能出现的所有 pattern 枚举出来。C-c C-a是自动搜索,让 Agda 在当前上下文中试着找符合条件的项,有时候能一把填出证明。C-c C-space是把当前 hole 里的代码“交付”给 Agda 检查,相当于确认这一项可以填进去了。
另外还有两个用得不少的命令:C-c C-r是 refine(精化),把 hole 的位置替换为一个函数应用的骨架,比如目标是a + b时,它会先把_+_拿出来,让 Agda 生成两个子 hole 给你填;C-c C-n是规范化,输入一个表达式,Agda 会把它约简到最简形式,这个在验证两个表达式是否等价时极其好用。
我个人的建议是:先在纸上把这七个命令抄一遍贴在显示器旁边,前几次写代码时逐个试,基本两三次就能形成肌肉记忆。真记不住也没关系,VS Code 命令面板搜Agda,所有命令都在那里,点开就能用,只是速度慢一点而已。
3.3 实操示例:亲手写一个小证明
现在来点实战。假设要证明:如果两个自然数相等,那么它们的后继(加一)也相等。这个命题在 Agda 里写成:
module proof where open import Agda.Builtin.Nat infix 4 _≡_ data _≡_ {A : Set} (x : A) : A → Set where refl : x ≡ x suc-inj : {n m : Nat} → n ≡ m → suc n ≡ suc m_≡_是自定义的相等类型,它的唯一构造函数refl表示某个值等于它自身。要证明suc-inj,思路很直接:如果n ≡ m是由refl构造出来的,那就意味着n和m原本就是同一个值,那么两边加suc自然是相等的。
考虑先写一个 hole:
suc-inj p = {! !}保存后C-c C-l加载。此时你会看到蓝色高亮的 hole,下面信息面板显示目标类型是suc n ≡ suc m,且上下文中有p : n ≡ m。光标移进 hole,按C-c C-c,扩展提示 split on,输入p回车。Agda 自动把p模式匹配为refl,同时把上下文中的n和m统一成同一个变量,目标自动变成suc n ≡ suc n:
suc-inj refl = {! !}这时再按C-c C-a自动搜索,Agda 会找到refl填进去,因为suc n当然等于它自身。最后C-c C-space确认,整个证明完成:
suc-inj refl = refl整个过程几十秒钟,但每一步都在跟 Agda 互动、看反馈,这就是交互式证明的乐趣所在。
3.4 悬停、规范化与类型推导的进阶小技巧
除了上面这些带快捷键的操作,VS Code 的 agda-mode 还支持鼠标悬停显示类型:把鼠标悬停在任何一个表达式、变量或函数名上,会弹出它的类型信息,这个功能在老 Emacs 里要额外配置才有类似体验,但在 VS Code 里原生就有了,对于阅读别人的 Agda 代码尤其好用。
C-c C-n在我实际写证明时出镜率很高。比如我想验证一个表达式是否能够化简成期望的形式,就把光标放在一个 hole 里,按C-c C-n,输入suc (zero + suc zero),Agda 会计算并返回suc (suc zero),即2(用suc和zero表示的自然数)。这个命令帮你省掉很多手动推导,尤其是遇到复杂递归函数的时候,“让机器先算一遍”能极大提高调试效率。
C-c C-t除了查询类型,还有一个小妙用:在输入表达式的半成品时它能提示你当前上下文和预期类型。比如你正试图填充一个目标类型为a + b ≡ b + a的 hole,按下C-c C-t,底下会完整显示当前的假设、可用变量和最终目标,相当于给你一张地图,不会写着写着迷路。
4. 踩坑实录与效率技巧
4.1 常见问题排查速查表
我把自己和其他用户遇到的典型问题整理成了一张表,遇到问题先对照排查。
| 现象 | 原因 | 解决办法 |
|---|---|---|
加载文件报agda: command not found | Agda 不在 PATH 中 | 检查安装路径,Windows 下重装官方包,macOS/Linux 下确认agda --version能正常输出 |
| 已装新版 Agda,但扩展报版本不兼容 | VS Code 扩展缓存的版本信息过期 | 重载窗口,更新扩展到最新版;如仍不行,删除~/.agda下的缓存文件后重试 |
输入\to按空格不转换 | 文件未加载,或扩展未激活 | 先C-c C-l加载;确认扩展已启用;确认文件扩展名是.agda |
| 符号显示成方框或乱码 | 当前字体缺少对应 Unicode 符号 | 设置编辑器字体为 Fira Code、DejaVu Sans Mono 等,见下面细节 |
扩展一直显示Loading...,但迟迟没有结果 | 文件里存在编译错误,导致进程卡死 | 按Esc取消当前操作,修正语法后重新C-c C-l;如果还不行,重载窗口 |
| 鼠标悬停不显示类型信息 | 文件尚未成功加载 | 先执行C-c C-l成功加载后再悬停 |
打开了多个.agda文件,互相干扰 | 扩展为每个工作区维护一个 Agda 进程 | 尽量一个 VS Code 窗口只开一个 Agda 项目,或分开工作区 |
这里要多说一句:Agda 的报错信息初次接触会觉得相当“劝退”,满屏的Setω、黄色波浪线、看似莫名其妙的缩进错误。但它的错误提示其实非常精确,只要耐心从上往下读第一条报错,再配合 VS Code 的悬停功能,绝大多数问题都能自己定位。
4.2 如何让数学符号在 VS Code 里正常显示
Agda 代码里大量使用→、λ、ℕ、⊎这类 Unicode 字符,如果 VS Code 当前字体不支持,屏幕上就会出现一堆方框或替代字符,看着难受,阅读效率也很低。解决办法是给 VS Code 配置一个支持范围广的字体优先列表。
我现在的配置是把editor.fontFamily设置为'Fira Code', 'DejaVu Sans Mono', 'Noto Sans Mono', monospace。Fira Code 的优点是除了符号全,还支持连字,↯这类 Agda 里常用的“矛盾”符号渲染得很漂亮;DejaVu Sans Mono 则是最稳妥的兜底选择,几乎各种 Linux 发行版都会自带,覆盖符号也很全。在 Windows 上如果安装了 Windows Terminal 或 Office,同时会把Cascadia Mono带上,这个字体对数学符号支持也不错,也可以排在前面。
想在 VS Code 里改字体,按Ctrl+,打开设置,搜索font family,把上述字符串填进去即可。改完之后如果符号还是显示成方框,那就是最常用字体都不含该字符,可以考虑装一个Noto Sans Math系统字体并在列表中加上它,基本能覆盖绝大多数情况。
4.3 我更习惯的几个小设置
用了一段时间之后,我总结出几个值得调整的地方,供你参考。
第一,把自动保存关掉或者谨慎使用。Agda 的加载是手动触发的,如果开了“自动保存”,你可能在打字过程中文件被反复写入,有时候扩展会在你还没写完一个表达式时就开始加载,报错刷屏,反而打断思路。我现在是手动保存,写完一个完整的构造再C-c C-l,思路更清晰。
第二,善用 VS Code 的工作区多根目录功能。当我同时需要参考标准库源码和手头项目时,把两个目录都加到同一个工作区,就可以在 Agda 代码里Ctrl+点击直接跳转到标准库定义,非常方便。
第三,关于标准库的加载路径问题。Linux 下如果从发行版仓库装的 Agda 和标准库版本不匹配,加载标准库时报错异常常见。最省心的方案是把 Agda 和标准库都用 ghcup 或源码统一编译安装,让两者的版本严格对应。Windows 上则建议直接下载官方安装包,里面通常已经带好了对应版本的 stdlib 配置脚本。
第四,如果频繁使用C-c C-a自动搜索,要注意它有时会把整个上下文里能匹配的项都尝试一遍,输出结果可能不止一个。如果自动搜索的结果不是你想要的,先按C-c C-rrefine 手动构造骨架,再配合C-c C-a填充子 goal,通常能更快找到正确路径。
写在最后的小体会
接触 Agda 有一段时间后,我最大的感受是:它与其说是一把“证明锤”,不如说是一个“思维训练场”。每次写证明,本质上都是在把你的推理过程拆解成类型系统能验证的步骤,这会让你的逻辑表达变得非常严谨。VS Code 上这个 agda-mode 扩展虽然还做不到 Emacs 版 100% 的功能覆盖,但对普通用户而言,日常写证明、做习题、读标准库已经完全够用。如果你是从 Emacs 转过来的,操作逻辑几乎是无缝迁移;如果你是第一次接触 Agda,也建议从 VS Code 入手,把学习成本降到最低。按照本文的流程装好环境、跑通第一个证明,后面怎么走,就是你自己的事了。
本文还有配套的精品资源,点击获取