用 OCaml 学编程
文章摘要
《Learn Programming with OCaml: Algorithms and Data Structures》是法国研究者 Sylvain Conchon 与 Jean-Christophe Filliâtre 合著教材的英文版,由 Urmila Nair 翻译,OCaml 软件基金会出资并促成开放获取。全书以 CC BY-SA 4.0 授权免费发布,提供 PDF 与 EPUB 两种格式,以及全部程序的源码下载;勘误通过 GitHub issue 收集。当前版本编译于 2026 年 2 月。
作者的写作主张
序言开门见山:学编程很难。要编好程序需要算法知识、想象力、预见力和对语言的掌握,但最重要的是经验,因为困难往往藏在细节里。作者强调风格是编程的本质——同一个算法在同一种语言里可以有多种写法,其中只有少数能同时做到优雅与高效,而那正是程序员应当追求的。正因为这个信念,他们选择用一门具体的语言(OCaml)而非伪代码来呈现全部内容。
三部分结构
- 第一部分「用 OCaml 编程」:面向初学者,既包括零基础者,也包括不懂 OCaml 的资深程序员。这部分不讲抽象语法,而是靠一连串小程序把语言和编程的基本概念带出来——闰年判断、蒙特卡洛方法、绘制心脏线、曼德博集合、埃拉托斯特尼筛法、绘制曲线、复制文件、倒转文本行序、任意进制整数转换、打砖块游戏(分两节)、Logo 海龟绘图、演奏乐谱、四叉树、解 N 皇后问题。随后单列一章深入 OCaml 概念:类型检查算法、运行时模型、以及如何分析程序的执行时间。
- 第二部分「数据结构」:数组(可变长数组、位向量、Rope、持久化数组)、集合与字典(二叉搜索树、AVL 树、哈希表、前缀树、Patricia 树)、队列(命令式队列、持久化队列、命令式优先队列、持久化优先队列)、图(邻接矩阵、邻接表、邻接字典,并做性能对比)、并查集、Zipper(列表上的、树上的,以及游标)。
- 第三部分「算法技术与应用」:算术(欧几里得算法、快速幂、模运算、矩阵计算)、动态规划与记忆化(含机械化记忆化与 hash-consing)、排序算法(插入、快排、归并、堆排,及最优复杂度与实验评估)、图算法(广度优先、深度优先、最短路、最小生成树)。
全书共 103 段带编号的程序,每一段都在配套网站上列出了页码并可单独下载或打包获取,全部在 OCaml 4.14.0 上编译测试过。各章末尾都配有习题,正文约 450 页。
一个值得注意的编排特点是:接口签名被当作头等内容来讲。目录里反复出现「可变长数组的最小签名」「位向量的最小签名」「持久化集合的签名」「有序类型的签名」「命令式集合的签名」「哈希表元素所需的签名」等条目——作者显然是在用 OCaml 的模块系统把「先定义抽象接口,再给实现」这件事贯穿到每一种数据结构的教学中。
定位
作者自述本书面向教师、学生和希望有效使用 OCaml 的程序员。对教师和学生而言,它可作为算法课或函数式编程课的教材;对 OCaml 程序员而言,它可作为查阅特定数据结构或算法的参考书。书中明确说明,所有程序都可直接复用。
抓取说明:usr.lmf.cnrs.fr/lpo/ 只是一个下载落地页,正文在 PDF 里。以上内容基于该 PDF 的目录与序言,以及配套的代码索引页整理。
HN 评论精华
317 分、140 条评论。有意思的是,整场讨论里真正谈这本书内容的部分很少,绝大部分篇幅被两个跑题但更有生命力的问题占据:LLM 时代还要不要学新语言,以及第一门编程语言该选什么。
「LLM 都会了,我还需要学吗」
throwaw12 用一个自称真诚的提问点燃了全场:最近他总在问自己,我需要学这个新东西吗?该逼自己学吗?反正 LLM 都会。
yawaramin 一句话概括了这条回复串的命运:「在 Hacker News 上发帖问『我们为什么要学一门编程语言』,是顶级的钓鱼。」
回复大致分几路。类比路线最热闹:adalacelove 反问「历史已经写在书里了,你还要学历史吗?别人已经在活着了,你还要活吗?」;ahoka 问「既然可以直接按播放键,为什么还要学乐器?」;andai 问「既然技术能把我从一个地方送到另一个地方,我还该继续走路吗?」
a2ff6eeb0 给乐器那条提供了一个冷峻的反驳,也是讨论里最不合群但值得认真对待的一条:直到不久之前,你都无法「按下播放键」就获得丰富的音乐选择——历史上大部分时间里,想听音乐要么自己演奏,要么雇人;更晚近一些也得去商店买,选择有限。今天学乐器是为了社会地位,继承的是过去那种音乐稀缺而昂贵的光环,人们学它是因为那个旧情境被浪漫化了。他预测大概需要一代人的时间,人们才会毫无负罪感地享受无限的、完全生成的音乐。pessimizer 的回击很干脆:学乐器的理由是演奏乐器本身就直接令人愉悦——「你这是消费者/观众脑。我是说,既然你可以只读评论,为什么还要发评论呢?」
aaplok 贡献了最受欢迎的类比:他喜欢烘焙。工业机器能以他无法竞争的规模烤面包,家用揉面机也做得很好,他自己多数时候就用后者。但他仍然认为手工揉面值得花时间——它让他了解面粉的各种性质、湿度和温度这些外部因素如何影响整个过程,而他相信这让他成为更好的烘焙者即使在使用机器时也是如此,因为他更擅长控制机器在做什么。拿到一个新牌子的面粉,他一定先全手工做一遍来「找感觉」。编程也一样:如果你讨厌写代码,那机器产出的「够好」质量大概就是你需要的全部;但如果你享受写代码,它应该让你变得更好,即便在你使用机器的时候。
实用派的回答则集中在「你得看得懂输出」:bigstrat2003 说 LLM 不知道任何事情,因此会犯错,你必须能检查它们的工作;zem 说如果你对 LLM 写的代码没有扎实的心智模型,最终会非常难受,更关键的是你得能驾驭 LLM 让它的代码符合你的心智模型;sgt 指出一个代际问题——你之所以能指挥 LLM 编程,正是因为你懂编程和架构,那么跳过了学 OCaml、C、Python 这一步的未来几代人怎么办?
rramadass 提出了讨论中最完整的认识论论证:人类「理解」,而 LLM 只是「知道」。跨学科的洞见、新的思维模式、灵光一现仍只属于人类;AI 能帮助聚焦和缩短学习路径,但理解只能发生在人的头脑中——如果你连基本的领域知识都没有(即「未知的未知」),你怎么可能向 LLM 提出正确的问题?他举了牛顿从测量连续变速运动的需要中发展出极限与微积分、凯库勒从梦见衔尾蛇得到苯环、笛卡尔为用代数解决几何问题而发明坐标系作为例子,认为这些都是把不同概念汇聚起来创造全新知识领域的跳跃。
支线争论:注意力与阶级
xiaoyu2006 说你也可以纯粹为了自娱自乐、为了理解本身的乐趣而学,很多数学家就是这样驱动的,「当下的社会价值观把太多功利主义压在学习上,这是种耻辱」。
这引出了本串最激烈的对撞。Barrin92 认为这不是稀缺问题:普通人每天在智能手机上花六小时,TikTok 用户光在那一个 app 上就花 100 分钟——「这不是人为稀缺,这是普通人活得像《机器人总动员》里的人」。Avicebron 反驳说逃避现实和集中精力做一件事是两回事,问这个问题的人真正在说的是「学 OCaml 能不能帮我拿到那份六位数、有医保的工作,让我不必在边缘苟活」,而不是「我只做能赚钱的事」。
cellis 则给「低质量娱乐」这个说法贴上了精英主义标签:大多数人没有时间或幸运通过东岸文理学院被引介去读托尔斯泰或欣赏柴可夫斯基的细腻;他那一代人玩策略游戏和第一人称射击、听阿姆,这一代刷 TikTok 刷到拇指劳损。他对学 OCaml 的实际建议相当务实:绝大多数聪明勤奋的人,与其试图去 Jane Street 之类的地方找工作(「世界上最好的 1% 工程师才能用这门语言工作」),不如去把 AI 用到极致、做点 agent 服务的生意。srean 补了一句好话:许多经典当年都是流行读物,标签是后来才贴上的——经典与流行文化未必对立。
OCaml 与 LLM:本场最实质的技术讨论
giraffe_lady 抛出了一个有分量的断言:OCaml 是当下 LLM 的秘密武器,模型写 OCaml 比写任何其他语言都好。他补充说其他「纯」Hindley-Milner 语言基本打平,但 OCaml 有些特殊优势。
nine_k 追问了两个好问题:为什么不是 Rust?为什么不是 Haskell?giraffe_lady 的回答是:两者都逼模型在类型上思考太多。OCaml 那种更受约束的类型系统加上全局类型推断,只是把模型限制在它已经写下的东西上,而且反馈极快。Rust 在你确实需要时是好的,但多数东西不需要,而 OCaml 对「常规」工作有更便利的抽象,性能也不差太远。至于 Haskell,类型系统表达力太强,它本身就变成了 agent 犯错和陷进去的地方。
yawaramin 从工程角度给出了另外两条理由,我认为是整场讨论中最可操作的:第一,编译速度——OCaml 编译极快(接近 Go),这带来快速反馈循环,让 LLM 能一路推进实现。第二,OCaml 有个惯例,凡是能被库外访问的模块都写接口文件(.mli),而接口文件恰好给了 LLM 一份关于「要什么」的精确摘要,模型只需跟着类型填空就能完成实现——「这几乎是完美的用例」。
phtrivier 追问这件事有没有被真正研究过。giraffe_lady 回答说自己今年春天为一篇相关论文贡献过代码,但这很难测量,而所谓「研究」就是往前沿模型里喂钱;原计划六月发表,他自己也还在等。当被问到是否是个人体验上 LLM 写 OCaml 优于 Python、Java 这些训练数据远更充足的语言时,他澄清了关键:这无关训练数据量,而关乎反馈循环的速度和质量——你要的是那个在给定最少信息时能抓住最多问题的编译器。
mchaver 补充了一条前端向的观察:OCaml 的衍生语言 ReScript 在 Web 前端同样是秘密武器,因为类型、快速编译加上对 React 的一等支持,「用 ReScript 写 React 比用 TypeScript 舒服得多」。
第一门语言之争:ML 还是 C
tialaramex 提出了一个界定清晰的主张:ML 应当是计算机科学家的第一门语言。他特意区分了两种情况——对于那些编程将会是他们唯一语言的人(今天常见 Python,统计领域常见 R,也见过 Java),该教什么并不清楚;但专门作为第一门语言,他毫不怀疑应该选 ML,尽管他所在的大学几十年来一直教 Java、近年教 Python。他还主动声明自己当年学的是 Standard ML of New Jersey,但认为这段经历并未过度影响他的判断。
MeetingsBrowser 提出了整场最持久的反对意见:ML 对理论和抽象面很好,但 C 无疑是让人感受计算机如何运行程序的最佳语言——它仍是抽象,但至少让人意识到内存管理、复制与引用等 ML 语言会隐藏掉的重要概念。
这条引发了一场关于「C 到底有多贴近机器」的深度混战:
xiaoyu2006反驳说 C 本身也是重度抽象的,现代操作系统与 C 协同演化,以至于表现得像一个模拟 PDP-11 的 C 运行时;要学系统你需要的是操作系统课程,不是 C。siev补上了他想引用的那篇文章(《C 不是低级语言:你的计算机不是一台快速的 PDP-11》)。uecker对这个说法提出了有力的异议,而且是讨论里最专业的声音之一:PDP-11 这个梗相当误导。C 的编程模型里没什么是 PDP-11 特有的,C 也被用在比 PDP-11 古怪得多的系统上;C 还成功成为多处理器系统上大量并行程序的基础,其内存模型的制定远晚于 PDP-11。更重要的是,「硬件和操作系统为了 C 而模拟 PDP-11」这个隐含论证是完全倒过来的——历史上确实有过别的路线,比如为面向对象或 actor 模型设计的处理器,它们都不成功。他后来还补充说,那篇文章的许多批评如果把「PDP-11」换成「冯诺依曼架构」依然成立,这让整件事显得有点怪。nivertech贴出了一张 PDP-11 指令与 C 语法的对照表(自增自减、各种寻址模式对应指针操作)来支持影响论。uecker的回应划出了他真正的界线:他不否认某些表层特征受 PDP-11 启发,他反对的是这与计算架构的根本无关;而且这恰恰与「今天一切都因 C 而模拟 PDP-11」的说法自相矛盾——几乎没有架构具备那种自动增减寻址模式,尽管 C 有对应的原生语法。adrian_b写了全场最长也最激进的一条:要感受 CPU 如何运行你的程序,你需要学一门现代汇编(x86-64 或 AArch64,想简单点可以从 POWER 开始)。C 使用的硬件机器模型已经完全过时,现代 CPU 一多半以上的指令无法用 C 直接表达,C 语言甚至没有许多硬件指令使用的数据类型。他认为关系恰恰是反过来的:一个懂 CPU 如何运行程序的人会更容易理解 C 程序如何运行;但只懂 C 的人,对非平凡程序执行方式的心智图像很可能大错特错。他甚至认为学汇编之前先学 C 是负担而非帮助,会养成坏习惯(不当的隐式转换、不合适的整数类型、不关心缓存交互和访存顺序、在现代 CPU 上用指针访问数组反而更低效等)。他赞同 ML 作为第一门语言,并认为连古老的 Fortran 都比 C 更合适,因为它对现代 CPU 的误导更少。krapp的一句话被广泛转述:C 几十年来都没有呈现计算机实际工作方式的准确模型,它是一门伪装成低级语言的高级语言。ahefner给出了我认为最公允的回应:计算机本身几十年来也没有呈现计算机实际工作方式的准确模型——对比 90 年代中期以来任何主流产品的表层 ISA 与其底下发生的超标量和乱序执行就知道了。刻意把这个更深层次暴露回程序员可见 ISA 的努力(VLIW,尤以安腾为著)在小众应用之外惨败。C 和它的竞争者无法绕过呈现给它们的 CPU 抽象,而它已经出人意料地优雅地从 16、18 位机器演化到 32、64 位机器,甚至通过 intrinsics 相当体面地容纳了 SIMD。
关于教学的一条务实证词来自 jech,他来自巴黎西岱大学:对本科教学来说 C 足够好了——在 C 里你可以轻易解释一个结构体定义和它在内存中的布局之间的关系,这在 Caml 里困难得多(代数数据类型和内存布局是什么关系?),在 Java 里也是(它引入了你从没要过的指针)。他们的做法是第一年教 Java,第二年教 C 和 Caml,效果看起来不错。tialaramex 承认结构体布局规则确实是教学上的福音。
astrobe_ 提出了跳出二选一的看法:标题把大家引入了虚假的两难。如果有人学编程不是为了达成某个目的,那他不该只学一门语言,而应该每个主要范式学一门——C(命令式/过程式)、Smalltalk(面向对象)、Prolog(逻辑)、SQL(声明式)、OCaml(函数式)、Forth(连接式,同时可以窥探解释器/编译器/虚拟机的内部)。debo_ 的版本更精简:深入理解一门 C 系语言、一门 Lisp、一门 ML 系语言就够了,这三大家族影响了太多东西,熟悉它们会让理解各种语言和库容易得多。penteract 则提出了一个元层面的担忧:第一门语言的影响可能不如大家想的那么大,最大的影响或许只是决定了这个人会不会继续学下去;而且如果所有计算机科学家都太相似,我们可能会失去一些东西——有 Lisp 信徒,也有 C 的拥趸,任何一方缺席都会让这个领域变得贫瘠。
关于这本书本身的评价
真正针对书的评论不多,但相当关键。
jan_m_savage 提出了最重要的一条批评:这本书虽然在其他方面很优秀,但对初学者来说太难了——节奏太快,如果你还不会编程,它在教学法上远谈不上有用。ratorx 附议并给出了依据:它显然不假设零基础,而这正是人们对一本「第一门编程语言」的书的期待;不过书的引言确实说明了它更多是面向算法课程的。jan_m_savage 的回应点破了要害:那么书名就有些误导了。
ratorx 顺带给了一条我认为很好的建议:对绝对的初学者来说,REPL 是最好的学习环境——你能得到即时反馈,可以完全专注在短短几行真实代码上,不必操心编译器、构建系统和整个操作系统;如果再配个图形界面外壳,甚至可以完全跳过终端。
netfortius 提出了一个没人回答的实际问题:法文原版似乎已有十二年(2014 年),这个英译本是否是更新过的版本?
sweetjuly 提出了一个有趣的设想:他很好奇第一次学编程就用 OCaml 会是什么体验。他自己是写了很久 C 之后才学 OCaml 的,痛苦地花了很大力气才改变思考算法的方式,翻过那道坎之后反而改变了他写 C 的方式(「变好了?」)。nestorD 现身说法回答了这个问题——他正是法国教育体系路径的产物,在没有网络连接的情况下读完一本类似的书。头几周很难熬:他当时不太清楚「类型」是什么,而编译器的报错在缺少这层背景时既无情又难懂。但一旦领会了核心思想(对递归能做什么的正确理解花了更久),后面出奇地顺畅。他把自己成为更好的程序员归功于这段经历。补充一点背景:他 18 岁高中毕业后才开始编程,「按 HN 的标准算晚的」,而一些先玩过 Python 的同龄人则讨厌 OCaml。
最打动人的一条来自 lefessan:他指出一个常见的误解是把 OCaml 归类为函数式语言,它其实是多范式语言——命令式、面向对象、函数式,你想用哪种范式都行。他自己的用法是 OCaml 混合 C 存根(用于绑定 Linux 库),用来替代 shell 脚本、Python 脚本、JavaScript/TypeScript,也用来写大型应用(他很久以前用 OCaml 写过一个 Emacs 克隆,后来写了 MLdonkey)。他的总结是全场引用最多的一句:一旦你开始用它,它不会取代你的主力语言,它会取代你需要的所有其他语言。
phtrivier 贡献了全场最有文学性的一条,是一份跨越 25 年的法国工科教育回忆:大量数学,然后更多数学;用(法语的)伪代码写算法;把 Caml 当作「可执行的伪代码」,对使用可变状态和副作用带着警告和一丝嫌恶;大量需要在纸上一次写对、缩进正确的考试,对着各种递归树做各种可怕的操作,而阅卷人在做「代码评审之母」;到了工程学校再重来一遍,因为三分之一同学完全没学过计算机科学,另三分之一学的是 Pascal;然后学 C 和汇编,思维被朝完全相反的方向炸开;最后看着老师们不情愿地说「你们还是学 Java 吧」,因为「业界用的是那个」,「反正没人能靠写 Caml 找到工作」。他的结尾很妙:25 年后——确实有人靠写 Caml 的一种方言,在某家给几十亿人推送猫图和种族主义梗图的车库创业公司找到了工作;26 年后——「你还是学怎么给 LLM 写提示词吧」,因为「业界需要的是那个,而且再没人能靠编程找到工作了」。
其他资源推荐:多位评论者(henryrobbins00、__rito__、Fervicus)都提到了康奈尔大学 Michael Clarkson 的 CS3110 课程教材《OCaml Programming: Correct + Efficient + Beautiful》,__rito__ 称其为「我所知最好的编程学习资源之一」并表示自己完整读过。zvmaz 推荐了 OCaml 创造者 Xavier Leroy 的访谈视频;rramadass 补充说 Ryan Peterman 那整个语言设计者访谈系列都值得看,并给出了一个有趣的观察——语言设计者们往往只反复强调概念基础并逐步在其上构建,而程序员(哪怕是知名的)的访谈里则常见大量关于语言复杂特性的花哨谈论;他认为专家有一种在头脑中组织领域知识的方式,让他们直觉地知道什么是麦子什么是糠,而这才是我们需要学的东西。
技术性小插曲:veqq 问为什么 PDF 比 EPUB 还小。shakna 给出了准确答案:EPUB 里同时包含 SVG 和 PNG,而后者在 EPUB 本质上使用的「普通 zip」下压缩效果不佳;PDF 则可以对所有对象流使用 zlib,得到还算不错的压缩率。
唱反调的:iLoveOncall 只留下一句「别用 OCaml 学编程,它糟透了」;shevy-java 认为 OCaml 是门非常奇怪的语言,不认为它能复制 Python 那样的成功故事,debo_ 的回应很平实——「OCaml 到这个岁数已经很老了,它永远不会成为 Python,但我也不觉得它会消失。」jiehong 留下了一句简短但真实的批评:标准库还可以更好。lenkite 则说明了自己为什么选了 Lean 而非 OCaml 或 Haskell:更好的类型系统(依赖类型!)、能用于形式化领域,还能顺便学数学。