mcpField
O
ocaml

ocaml

github.com/bsene开发762 次安装3 天前更新
代码仓库

关于 ocaml

面向 OCaml 学习者的语言技能指南,重点解释惯用写法背后的原因,并涵盖类型推断、运行时模型、项目结构与测试。

主要功能

  1. 01类型推断与编译器错误解读
  2. 02模式匹配与函数式惯用法
  3. 03运行时、相等性与可变值
  4. 04Dune 项目和 Alcotest 测试
  5. 05数据结构与算法指导

适用场景

  1. 01从 JS、TS 或 PHP 转向 OCaml
  2. 02理解类型推断错误和编译器消息
  3. 03搭建 Dune 项目并编写测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/bsene/skills --skill ocaml

安装后在 Claude Code 中输入/ocaml确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
benchmarks.md
core-language-cheat-sheet.md
data-structures-and-algorithms.md
dune-and-alcotest.md
inference-and-compiler-errors.md
learn-programming-with-ocaml.md
learning-resources.md
representation-and-invariants.md
runtime-model.md
testing-and-quality.md

详细介绍

OCaml

OCaml 是一种静态类型、以函数式为主的语言,拥有强大的类型推断引擎(Hindley-Milner)。如果你使用过 JS/TS/PHP,最大的思维转变并非语法,而是要认识到:编译器是设计伙伴,而不是代码检查工具:穷尽式模式匹配和健全的类型系统能在编译时捕获整类错误(空值检查、遗漏分支),而这些错误在你熟悉的语言中通常要到运行时才能发现,甚至根本无法发现。

默认应解释某种写法 为什么 符合惯用风格,而不只是说明 它是什么 ——用户是在学习这门语言,而不只是转换语法。

类型推断:除非用户要求,否则不要添加类型注解

OCaml 几乎总能正确推断类型。除非用户要求添加类型注解(或 .mli 要求),否则编写绑定和函数时不要添加类型注解,例如 let total_ttc price_ht tax_rate = price_ht *. (1. +. tax_rate)。要查看推断出的类型,可以使用 ocamlc -i file.ml、dune utop 或顶层交互环境。阅读类型推断错误时,应按顺序梳理约束:报告的错误是最后一个无法满足的约束,不一定是出错的位置。示例和完整方法:类型推断与编译器错误。

核心语言要点

  • let 是不可变的;需要显式使用 ref 才能进行变更——应优先考虑重新绑定或递归。
  • match 会检查穷尽性;变体类型取代 TS 联合类型和枚举,记录类型取代对象接口,option 取代 null,result 取代用作控制流的异常。
  • 异常不会被类型系统追踪;对于预期会发生的失败,默认使用 option/result。
  • 所有函数都是柯里化的;|> 用于管道传递;递归需要 rec;优先使用带累加器包装函数的尾递归,并且绝不要在循环中使用 acc @ [x]。
  • = 表示结构相等,== 表示物理相同——这与 JS 开发者的直觉相反;几乎总是应该用 =。
  • int 是 63 位整数,溢出时会静默回绕;int 和 float 永远不会混用(+., -., *., /.)。
  • 模块和 .mli 签名用于定义接口和隐私;相比类,应优先使用模块、记录和函数。
  • 参数的求值顺序未指定;使用 let ... in 强制指定顺序。

包含示例的完整速查表(折叠、; 顺序执行、异常、柯里化、尾递归):核心语言速查表。

运行时模型(解释相等性或变更时请使用)

任何关于 = 与 ==、变更或“为什么会这样”的回答,都必须以此模型为依据,并明确说明这一点——这不是可有可无的背景知识:

  • OCaml 值要么是未装箱整数,要么是指向堆中块的指针(由一个头字和 N 个值字组成)。
  • 元组、数组、记录和非常量变体构造器都只是块——{ x = 1.; y = 2. } 是一个指向 2 字块的指针,因此两个字段相同的记录仍然是分别分配的块。
  • ref 只是一个单字段可变记录,list 是由 2 字 :: 单元组成的链,闭包是包含代码指针及其捕获环境的块——这就是为什么 ref/:=/! 能与其他所有内容组合使用,而不需要成为独立的语言特性。
  • 因此,== 只比较两个字(时间复杂度为常数,即使面对循环值也是如此);= 会遍历块内部,遇到循环时可能永远无法结束。

完整说明:references/runtime-model.md。

项目结构与测试

  • 创建新项目:dune init proj <name>(必要时添加前缀 opam exec --),然后使用 dune build @check 和普通的 dune build 进行验证。dune build 会编译,dune runtest 会运行测试,dune exec ./foo.exe 会运行可执行文件。绝不要手动编辑生成的 .opam 文件;应编辑 dune-project。
  • 单元测试使用 Alcotest;实际项目还会添加基于属性的测试(qcheck)和变异测试(mutaml)。关于设置、stanza 和 Alcotest 结构:dune 与 Alcotest;关于测试类型的选择:测试与质量。

调试

将 dune build 的输出视为事实依据:类型错误通常指向真正的逻辑缺陷。规范消息、警告(8、10、11)和核心类型规则: 类型推断与编译器错误。

默认风格

  • 优先使用小型、可组合的函数,而非冗长的命令式代码体——优先使用 List.map/List.filter/List.fold_left,而不是用 ref 累加器手写循环;除非性能或清晰度确实需要后者。
  • 当分支超过两种时,优先使用模式匹配,而不是 if/else 链;尤其是处理变体类型时——这不仅更符合惯用写法,还能自动进行穷尽性检查。
  • 在合理情况下,让函数保持全定义(对非全定义情况返回 option/result),而不是对预期的失败情况抛出异常;异常应留给真正异常或程序员错误的情况。

表示形式与算法指导

  • 对于包含可变值的数组,请检查别名问题:Array.make n value 会共享同一个初始值。若每个元素都需要一个新对象,请使用 Array.init。
  • 对于需要强制保证不变量的情况,请使用抽象类型;若客户端需要检查值,但构造过程必须受控,则使用私有记录或变体。查看 references/representation-and-invariants.md,了解示例、持久性约定、比较器陷阱和有符号余数。
  • 对于自定义集合、递归记忆化、动态规划或图算法,请阅读 references/data-structures-and-algorithms.md。说明表示不变量、算法前置条件,以及最坏情况与摊销成本。

按需阅读

阅读时机链接
阅读类型推断错误、编译器消息或类型规则类型推断与编译器错误
解释源自 JS/TS/PHP 的语法或语义惯用写法核心语言速查表
解释 = 与 ==、可变性或值布局运行时模型
搭建 dune 项目或编写 Alcotest 测试套件Dune 与 Alcotest
选择 qcheck、变异测试、lint 或死代码工具测试与质量
学习编程练习、习题或从入门到编写真实程序的学习路径学习资源
书中的具体程序、数据结构或算法使用 OCaml 学习编程
基准测试结果基准测试

评论

支持 Markdown