OCaml 编译器 - 2021 年 10 月

我很高兴发布“OCaml 编译器开发通讯”的第四期。(这绝非详尽无遗:许多人最终没有时间写东西,这很正常。)

当然,随时欢迎您评论或提出问题!

如果您一直在开发 OCaml 编译器,并且想说点什么,请随时在此线程中发布!如果您希望我在下次准备通讯期刊(未来某个随机时间点)时与您联系,请通过电子邮件(gabriel.scherer at gmail)告知我。

往期期刊


2021 年 10 月对于我们中的一些人来说是一个特殊的月份,因为这是顺序冰川之前的最后一个月份——一项为期数月的冻结期,期间所有与多核无关的功能都将被冻结,以方便多核集成。

Xavier Leroy (@xavierleroy)

知道冬天即将来临,我在为发布 4.14 做准备时,解决了一些遗留问题,包括更多弃用警告#10675、信号处理的正确终止#10726,以及当操作系统允许时,增加原生堆栈大小限制#10736。后者应该可以缓解对于大型输入(遇到操作系统限制)的非尾递归代码“堆栈溢出”崩溃问题。

我还使用更现代的伪随机数生成 (PRNG) 算法重新实现了 Random 标准库模块。在RFC#28中,Gabriel Scherer 建议更改标准库 Random 模块的随机数生成算法,使其“可拆分”,以便在多核环境中提供更好的行为。(“拆分”随机数生成器状态会产生两个独立的状态,这些状态应该产生独立的随机数流;很少有 RNG 算法支持拆分,并且其理论也不被很好地理解。)

我的第一个提议基于 Xoshiro256++ PRNG,它速度快且统计上很强:#10701。但是,Xoshiro 不支持完全拆分,只支持一种称为“跳跃”的有限形式,讨论表明跳跃还不够。然后发生了奇迹:在同一时间(2021 年 10 月的 OOPSLA 会议上),Steele 和 Vigna 提出了 LXM,一个具有 Xoshiro 的所有良好特性并支持完全拆分的 PRNG 族。我立即使用 LXM 重新实现了 Random 模块 #10742,我发现结果非常好。我希望这种实现能够被选中来替换现有的 Random 模块。

构造函数模尾递归

Gabriel Scherer (@gasche) 在冰川截止日期之前完成了 TMC(构造函数模尾递归)PR 的工作 (#9760),这得益于与 Pierre Chambart (@chambart) 进行了精心安排的全天会议,Pierre Chambart 负责对这项工作进行了最后审查。他们设法得到了我们双方都喜欢的结果,现在该功能已合并到上游。

请注意,这是 Frédéric Bour (@let-def) 在 #181 中于 2015 年 5 月启动的 TRMC 工作的延续(Basile Clément (@Elarnon) 也做出了重大贡献);这次合并关闭了 OCaml 编译器最长的开发线程之一。

现在可以编写

let[@tail_mod_cons] rec map f = function
| [] -> []
| x::xs -> f x :: (map[@tailcall]) f xs

并获得一个有效的 map 尾递归定义。

手册中正在添加一个部分来描述此功能:#10740

(另一方面,构造函数拆箱工作没有进展,这将不得不等到 5.0。)

原生代码生成和链接方面的进展

作为RFC#15:使用 JIT 的快速原生顶层的一部分,在原生代码生成和链接方面,以及在 @NathanRebours 和 David @dra27 提出的原生顶层方面,进行了一系列小的更改:#10690、#10714、#10715

模块形状,以便更轻松地使用工具

Ulysse Gérard、Thomas Refis 和 Leo White 在 OCaml 编译器中提出了一种新的程序分析,旨在帮助外部工具理解实现文件(OCaml 模块的实现)的结构,特别是为了实现“定位定义”功能——在存在 includeopen 等的情况下,这并非易事。

他们的分析结果是一个“形状”,它以易于处理但结构丰富的形式描述了模块中的项(值、类型等)。

Florian Angeletti (@Octachron) 在冰川截止日期之前,凭借其出色的审查工作,允许合并此 PR。

(PR 的作者最初希望为 OCaml 编译单元添加新类型的编译工件,以便将形状信息存储在 .cms.cmsi 文件中,而不是过大的 .cmt 文件中。人们对此感到不满,因此目前已将这部分内容排除在外。)

Stdlib 中的 UTF 解码和验证支持

#10710 中,Daniel Bünzli (@dbuenzli) 添加了对 UTF 解码和验证的支持,这是标准库长期以来一直缺少的功能。该 API 经过精心设计,避免了分配和异常,同时提供易于使用的解码接口。

Seq.t 的便利函数

Seq.t 类型表示按需(但非记忆)的值序列,由 Simon Cruanes (@c-cube) 于 2017 年贡献,它只有最小的函数集,并且自那时起缓慢增加。François Potter (@fpottier) 和 Simon 在 @gasche、@dbuenzli 和许多其他人的审查下,在冰川开始之前,完成了对 40 多个函数的大量导入。这项工作始于 2020 年 2 月,得益于 Yawar Amin 的问题 #9312

请看

val is_empty : 'a t -> bool
val uncons : 'a t -> ('a * 'a t) option
val length : 'a t -> int
val iter : ('a -> unit) -> 'a t -> unit
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
val iteri : (int -> 'a -> unit) -> 'a t -> unit
val fold_lefti : (int -> 'b -> 'a -> 'b) -> 'b -> 'a t -> 'b
val for_all : ('a -> bool) -> 'a t -> bool
val exists : ('a -> bool) -> 'a t -> bool
val find : ('a -> bool) -> 'a t -> 'a option
val find_map : ('a -> 'b option) -> 'a t -> 'b option
val iter2 : ('a -> 'b -> unit) -> 'a t -> 'b t -> unit
val fold_left2 : ('a -> 'b -> 'c -> 'a) -> 'a -> 'b t -> 'c t -> 'a
val for_all2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val exists2 : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val equal : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
val compare : ('a -> 'b -> int) -> 'a t -> 'b t -> int
val init : int -> (int -> 'a) -> 'a t
val unfold : ('b -> ('a * 'b) option) -> 'b -> 'a t
val repeat : 'a -> 'a t
val forever : (unit -> 'a) -> 'a t
val cycle : 'a t -> 'a t
val iterate : ('a -> 'a) -> 'a -> 'a t
val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t
val scan : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b t
val take : int -> 'a t -> 'a t
val drop : int -> 'a t -> 'a t
val take_while : ('a -> bool) -> 'a t -> 'a t
val drop_while : ('a -> bool) -> 'a t -> 'a t
val group : ('a -> 'a -> bool) -> 'a t -> 'a t t
val memoize : 'a t -> 'a t
val once : 'a t -> 'a t
val transpose : 'a t t -> 'a t t
val append : 'a t -> 'a t -> 'a t
val zip : 'a t -> 'b t -> ('a * 'b) t
val map2 : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val interleave : 'a t -> 'a t -> 'a t
val sorted_merge : ('a -> 'a -> int) -> 'a t -> 'a t -> 'a t
val product : 'a t -> 'b t -> ('a * 'b) t
val map_product : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t
val unzip : ('a * 'b) t -> 'a t * 'b t
val split : ('a * 'b) t -> 'a t * 'b t
val partition_map : ('a -> ('b, 'c) Either.t) -> 'a t -> 'b t * 'c t
val partition : ('a -> bool) -> 'a t -> 'a t * 'a t
val of_dispenser : (unit -> 'a option) -> 'a t
val to_dispenser : 'a t -> (unit -> 'a option)
val ints : int -> int t

我们收到的一些来自新贡献者的优秀贡献

Dong An (@kirisky) 完成了 Anukriti Kumar 的一个长期未完成的 PR (#9398、#10666),以完成对 OCAMLRUNPARAM 变量的文档记录。

Dong An 还改进了 README 说明,其中描述了在 MacOS 或 Windows 上构建编译器代码库时应提供哪些 C 编译器:#10685

感谢 Wiktor Kuchta,ocaml 顶层现在在启动时会显示有关 #help 指令的提示,以获取帮助:#10527。(Wiktor 实际上已经不再是“新”贡献者了,在过去几个月里,他做出了许多优秀贡献。)

说到这里,来自 @sonologico 的一个 PR(于 2020 年 5 月提出)在几个月前才合并 (#9621)。它更改了 ocamldebug 调试器的内部构建系统,以避免在链接用户定义的打印代码时出现模块名称冲突。大部分延迟来自于维护人员就应该使用哪一种中的十二种名称冲突避免 hack^W 特性争论不休。