OCaml 局部函数类型:偏应用为何可能导致值逃逸

原标题OCaml Local Function Types: Why Partial Application Can Escape

Richard Eisenberg 用列表拼接示例说明,在 Jane Street 的 OCaml 编译器分支中,局部参数如何改变偏应用函数的模式。

RERichard Eisenberg讲者 · Jane Street
频道
Jane Street
发布
2024年5月29日
视频
9:14
阅读
4 分钟
幻灯片
4
原文
英语
在 YouTube 打开
往下滚动阅读时,视频会缩到右下角继续播放。字幕跟随阅读语言。
阅读语言共 2 种语言可读
由 AI 从英语原文翻译。打开“对照原文”可以逐段核对。
摘要

30 秒看懂

在这期 OCaml Unboxed 中,Richard Eisenberg 演示了局部模式、柯里化与偏应用之间微妙的相互作用。接受局部参数的函数在偏应用时可能产生局部闭包,即使书写的类型中没有明显标出这一点。 他解释了为什么将该闭包传给 List.map 会失败,展示如何用 eta 展开让例子通过编译,并说明在这一扩展中,函数类型里的括号为何会改变含义。

  1. 1整数示例为何可行第一个列表被复制进全局结果,但整数元素通过模式跨越而被视为全局值。将其换成不受约束的多态值会导致逃逸错误。
  2. 2偏应用捕获输入在这个例子中,List.map 需要一个全局函数。对 append_ints 的局部首参数进行偏应用会创建可能保留该参数的闭包。
  3. 3隐藏的局部模式多参数函数的局部参数意味着偏应用具有局部模式,即使没有显式写出这个模式。
  4. 4eta 展开将调用放进显式函数后,包装函数运行时会提供第二个参数。演示中的程序因此能够编译。
  5. 5括号会影响含义给返回的函数类型显式加括号而不写 local_,表达的是另一种模式,示例实现无法满足它。

要点建议

  1. 多参数函数的局部参数可能使其偏应用产生的闭包也成为局部值。
  2. 捕获局部值的闭包不能逃逸到生命周期更长的上下文。
  3. 整数可以跨越模式,因此示例中可以把局部列表中的整数放入全局列表。
  4. eta 展开通过显式提供剩余参数,可以避免造成问题的偏应用。
  5. 在局部模式扩展中,给返回的函数类型加括号可能改变它的模式和含义。
全文

按章节阅读整理后的全文

只看原始文稿
01

局部列表参数

Eisenberg 构建一个递归的整数列表拼接函数,其首参数为局部值。整数的模式跨越解释了为什么全局结果是安全的。

在这期 OCaml Unboxed 中,我要继续探索 Jane Street 的 OCaml 编译器分支中的局部模式,重点讨论函数类型。接受局部值的函数,其行为可能不会从写出的类型中立刻显现。

考虑一个递归函数 append_ints,它把一个整数列表拼接到另一个列表前面。如果第一个列表为空,就返回第二个列表;否则将第一个元素放在递归结果前面。尾递归可以改善实现,但不是这个例子的重点。

带局部参数的整数列表拼接
幻灯片 1 / 4带局部参数的整数列表拼接函数类型将第一个列表标为局部,而结果仍为全局。

我将第一个参数设为 local_ int list 类型,第二个参数和结果则为 int list。编译器接受了这个定义。第一个列表被复制进结果,第二个列表没有,因此值得问一问:局部列表中的元素为什么可以进入全局结果?

答案是这些元素是整数。int 可以跨越模式并被视为全局值,因此可以放进全局列表。如果把元素类型改为多态类型,编译器就会报告值逃逸了其区域:任意局部元素都不能安全地放进全局结果。

02

偏应用导致的逃逸

将偏应用后的拼接函数传给 List.map 会触发逃逸错误。闭包必须保留第一个参数,但 List.map 需要一个全局函数。

现在用 List.map 把 [1;2;3] 拼接到一个列表集合中的每个列表前面。将 append_ints [1;2;3] 作为函数传入会产生区域逃逸错误。编译器解释说这是一次偏应用,并建议再提供一个参数。

偏应用会构建一个函数对象,它必须记住第一个列表,直到第二个参数到来。这个对象可能存活很长时间。虽然我们知道 List.map 不会保存传入的函数,但标准库中的类型没有局部注解来保证这一点,所以它要求全局函数参数。

乍看之下,剩余的函数类型似乎是全局的,因为旁边没有可见的 local 注解。但闭包必须保留第一个局部参数。如果堆上分配的闭包可以指向局部栈上的值,该指针可能比区域存活得更久;模式限制正是为了防止这种情况。

03

隐藏模式与 eta 展开

偏应用保留局部参数时,其推断出的模式为局部。示例调用通过 eta 展开显式提供缺少的参数。

关键在于中间函数有一个隐含的局部模式。多参数函数接受局部参数时,其偏应用是局部的,因为闭包会保存这个参数。在该位置显式写出 local 注解不会改变含义,编译器已经推断出了它。

偏应用的逃逸错误
幻灯片 2 / 4偏应用的逃逸错误编译器报告偏应用后的拼接函数逃逸了区域,并建议补上剩余参数。

这个特定调用可以通过 eta 展开解决:不直接传入 append_ints [1;2;3],而是传入一个接受 x 并调用 append_ints [1;2;3] x 的函数。示例能够编译,因为包装函数内部的调用提供了全部参数。在这种上下文中,编译器可以将列表视为全局值,并在接受局部参数的位置使用它。

eta 展开后的调用与显式局部模式
幻灯片 3 / 4eta 展开后的调用与显式局部模式包装函数为 append_ints 提供两个参数,类型则显式展示局部的中间函数。

给函数类型加上括号时,这个区别就显现出来了。通常我们会把嵌套箭头理解为柯里化函数,但在局部模式下,显式加括号的形式并不等同于不加括号的形式。

04

括号改变函数类型

显式用括号括起返回的函数,隐含的模式不同于不加括号的形式。Eisenberg 解释了由此产生的错误及编译器提示。

括号与函数逃逸
幻灯片 4 / 4括号与函数逃逸给返回的函数类型加括号会改变其模式,产生另一条编译器错误。

如果括号内返回的函数没有局部注解,这个类型表示应用第一个参数后会得到一个全局函数。我的实现需要一个保存局部参数的中间堆闭包,因此编译器正确地报告:偏应用时函数或其参数之一逃逸了所在区域。

括号实际上在该位置表达了全局模式,尽管那里没有写出 global 关键字。原来不加括号的类型中,中间的局部模式是隐含的。这解释了为什么最初的偏应用会失败,以及为什么在包装函数中提供剩余参数就能通过。

由于这个偏应用问题困扰过许多用户,编译器为它提供了专门提示。这是局部模式中一个不同寻常的角落,也促使我们考虑为不允许偏应用的函数定义和箭头设计新语法。

同频道的其他文章

1:34:29EN
JSJane Street· 2026年6月24日Jacob Baskin on Building Superstore and Scheduling the HiveYaron Minsky, Jacob Baskin · Jane StreetThe Jane Street engineer traces the design choices behind a distributed analytical database and the challenge of allocating a growing research cluster.基础设施与部署Software engineeringDecision Making+1
1:34:36EN → 简体中文
JSJane Street· 2026年6月1日Nate Foster:把网络当作程序Yaron Minsky, Nate Foster · Jane Street从可编程交换机到 BGP,一位编程语言研究者解释高层策略和形式模型如何让网络更易于修改。基础设施与部署Programming LanguagesSoftware engineering+2
5 张幻灯片15:48EN → 简体中文
JSJane Street· 2026年5月15日走进 Jane Street 的得州 AI 数据中心Dwarkesh Patel, Yaron Minsky, Daniel Pontecorvo · Dwarkesh PatelDwarkesh Patel 与 Ron Minsky、Dan Pontecorvo 一同参观拥有 4,032 个 GPU 的训练设施,了解液冷、配电和运行保障。基础设施与部署AI cost & performance
7 张幻灯片1:20:57EN → 简体中文
JSJane Street· 2025年12月23日Matt Godbolt:深入 Skylake 的乱序流水线Matt Godbolt以逆向工程和细致测量为基础,导览指令解码、寄存器重命名、执行与内存顺序。SemiconductorsSoftware engineeringBenchmarks
1:12:58EN → 简体中文
JSJane Street· 2025年9月4日Chris Lattner:Mojo 与可移植 GPU 性能的追求Yaron Minsky, Chris Lattner · Jane StreetRon Minsky 与 Chris Lattner 讨论 Modular 如何通过语言和编译器设计,努力让加速器编程兼顾可移植性与速度。Programming LanguagesGPU programming & kernelsSoftware engineering+1
58:47EN → 简体中文
JSJane Street· 2025年7月28日Daniel Pontecorvo:为交易基础设施和办公空间降温Yaron Minsky, Daniel Pontecorvo · Jane Street从高密度 GPU 机柜到可移动办公桌,Jane Street 的物理工程团队围绕热量、电力、故障和人的舒适度进行设计。基础设施与部署Energy可观测性与可靠性+1