30 秒看懂
一位 Jane Street 演讲者介绍公司 OCaml 编译器实验分支中的 local 模式。他通过返回值、可变引用和文件回调等例子,解释局部参数向调用方作出的承诺,并为后续讨论内存分配做好铺垫。
- 1实验性模式local 模式是在 Jane Street 的 OCaml 编译器分支中开发的想法。演讲者先介绍其类型系统行为,再讨论它对内存分配的益处。
- 2不能逃逸的参数把函数参数标为 local,意味着该参数不能逃逸其区域。返回这个参数会产生编译器错误。
- 3存储也是一种逃逸将局部字符串存入外部引用也会使其逃逸,即使函数返回的是另一个值,编译器仍会拒绝。
- 4推理可变操作调用方可以利用 local 标注推断,函数在返回后没有保留第一个参数,从而分析之后修改该参数的影响。
- 5文件回调接收局部文件句柄的回调不能保留它,因此外层函数可以在回调返回后关闭文件。
要点建议
- 演示中的 local 模式来自 Jane Street 的实验性编译器分支,长期目标是将相关工作合并到上游。
- 局部参数不能逃逸其区域,例如不能通过返回或存入外部引用而逃逸。
- local 模式约束函数实现,同时允许调用方传入局部或全局参数。
- 不逃逸的参数有助于调用方推理后续修改以及回调中资源的生命周期。
- 该系列后续会把 local 模式与内存分配行为及延迟敏感型代码的性能联系起来。
按章节阅读整理后的全文
local 模式实验
演讲者介绍 Jane Street 的 OCaml 编译器分支中的 local 模式,解释为什么本集从类型系统行为开始。长期目标包括改善延迟敏感型代码的内存分配。
我想开始一个关于 local 模式的系列,它是在 Jane Street 的 OCaml 编译器实验分支中开发的想法。我们在这个分支中尝试和完善新想法,目标是最终将它们合并到主编译器。目前,我们可以在 Jane Street 的编译器中探索 local 模式。
我们会逐步了解 local 如何影响类型系统。最终的好处是改善内存分配行为,从而可能加快延迟敏感型代码。但我认为,先理解类型层面的行为,比直接讨论内存分配更容易入手。
我使用的是 Jane Street 版本的 OCaml 4.14.1。local 是一种模式,并不完全等同于类型,因此我不想称它为局部类型。示例中,我会为 let 绑定写出完整的类型标注:local 模式支持类型推断,但显式签名让例子更紧凑,错误信息也更清楚。
参数与区域逃逸
局部参数不能通过返回或存储到其他位置而逃逸其区域。演讲者展示编译器错误,并指出“每个函数都定义一个区域”这一简化规则存在例外。
先从一个简单函数开始,给它的参数加上 local 标注。这表示参数不会逃逸函数。如果函数返回这个局部参数,编译器就会报告该值逃逸了其区域:返回它违背了标注作出的承诺。

对于有两个参数的函数,标注可以只作用于第一个参数 x。返回 x 会产生逃逸错误,返回没有标注的第二个参数 y 则可以通过编译。我们对 x 作出了承诺,而不是对 y 作出了承诺。
区域是代码中的一片范围。目前可以先认为每个函数都定义了一个区域,不过其中有一个例外,我会在后续视频中解释。局部参数不能逃逸其区域,返回它就是一种逃逸方式。
把局部值存到其他地方也是一种逃逸。例如,如果 x 是一个局部字符串参数,而我把它赋给外部字符串引用,即使函数返回的是 y,编译器也会拒绝这次赋值。local 标注向调用方承诺参数不会逃逸。

推理可变操作与回调
不逃逸的参数有助于调用方推理之后的修改。文件回调示例展示如何防止局部句柄逃出预期生命周期。
这有什么用?假设某个类型有一个可变字符串字段,函数 f 将第一个参数作为局部参数接收。调用方可以用 t1 和 t2 调用 f,之后再修改 t1。由于 f 不能保留 t1,调用方可以推断,之后的修改不会影响 f 对该参数保留的关系或 f 返回的值。

局部参数对回调也有用。想象一个 with_file 函数,它接收文件名和一个回调,回调的文件句柄参数标为 local。回调结束后,外层函数知道回调没有存储或返回句柄,因此可以关闭文件。没有这一保证,我们就只能相信回调不会保留句柄。

对调用方的承诺
local 模式约束的是函数实现,而不是调用方可以传入的参数种类。局部和全局参数都可以传入。
这个标注约束的是函数实现,而不是调用方可以传入什么。局部参数既可以接收局部实参,也可以接收全局实参。不能返回或存储该参数,是函数向每个调用方作出的承诺;这只是探索的开始。
同频道的其他文章
1:48:29EN → 简体中文
1:36:37EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文
4:41EN → 简体中文