基本类型驱动的 ppx 重写

Basic type-driven ppx rewriting

我正在写我的第一个 ppx 扩展。这个想法是支持一个多态的 print 函数,类似于 Haskell.

中的 show

(我知道还有其他更强大的解决方案,但我希望了解更多有关其工作原理的信息。)

我采用的方法与描述的方法非常相似 here:我有一个映射器,它查找 %[print <expr>] 标签,然后用 [=14= 的字符串表示形式替换它们].例如,

[%print 1] ==> string_of_int 1
[%print "aksljd"] ==> "aksljd"

这适用于常量表达式,但我想支持任意表达式来代替 <expr>。它应该用最终类型的打印机包装它们。

我目前的做法是用Typecore.type_expressionParsetree.expression变成Typedtree.expression,然后匹配Typedtree.expressionexp_type字段,确定用什么替换整个表达式。例如,对于类型 type test = A of int | B of string,我会将 [%print A 1] 替换为 show_test (A 1)(按照惯例,show_test 必须存在)。

这行不通,因为 Typecore.type_expression 将类型环境作为参数,我在重写时无法获得 'current type environment',因为类型检查甚至没有然后执行... [%print 1 + 1]Typecore.type_expression Env.empty 导致 Unbound value +,这是应该的。

有人对此有解决方案吗?如果我完全朝着错误的方向前进,请随时指出这一点。 :p

你想做的事基本不支持ppx。正如您所指出的,ppx 是在解析之后,类型检查尚未完成。 由于编译器的当前体系结构,很难对任意表达式进行类型检查。

如果您想闯关,可以阅读 this。 :) 作者也将在 2 周后在 ocaml 研讨会上做一个关于它的演示,应该有一个视频。

评论中的讨论摘要:Haskell 中的 show 不是这样工作的(也不能)。 Haskell 中的 Show a => 将被转换为 OCaml 中的显式模块参数。该模块将有一个带有签名的值,如 print : a -> string。 Haskell 通过为您(或库)声明的 a 找到 Show 的实例来推断此模块参数,但在 OCaml 中,您必须手动传递该模块。在 Haskell 中声明 Show 实例类似于在 OCaml 中实例化仿函数。

在 Haskell 和 OCaml 中,问题中的预处理表达式在以下情况下具有可疑的含义:

let f x = [%print x]

如果 f : 'a -> string(即没有 Haskell 类型 class 实例可用并且没有传递 OCaml 模块)。

为了使其更完整,许多其他分析类型的 ppx 重写器从类型定义或声明而不是表达式生成代码。

大家已经在一定程度上回答了...希望我的回答能有所补充。

PPX 是 untyped AST 的预处理。它应该用于在不知道输入类型的情况下进行 AST 转换。

ppx_deriving 和其他一些 ppx 预处理器为每个类型定义和构造函数自动生成代码。它们确实很酷,但它们纯粹在无类型层内工作:它们只是利用类型定义的句法结构。对于类型依赖行为,您需要在非类型化 AST 中显式提供类型信息:[%derive.show: (int * int) list] [(1,2); (3,4); (5,6)][%derive.show] [(1,2); (3,4); (5,6)] 无效。

因此,PPX的I/O是untyped的,但不限于里面是untyped的。你可以做任何你喜欢的打字。一个典型的例子是将未类型化的输入提供给 OCaml 类型检查器并获得 typed AST,然后将获得的类型信息用于您的目的。这种类型化 PPX 所需的工具已经在我们手中:ppx_tools 用于通用 PPX 工具,compiler-libs 用于通过 OCaml 类型检查器和预处理进行类型化,untypeast.ml 用于将结果返回到未类型化的 AST。 link (https://bitbucket.org/camlspotter/compiler-libs-hack) @Drup 指出解释了如何使用它们。是在PPX出来之前写的,在PPX里面你做的都差不多,我还在文章里加了一小段PPX。

typeful PPX 的唯一缺点是它不能与 REPL 一起使用。 PPX 一个一个地处理编译单元,每个单元处理之间的传递信息是有限的。在 REPL 中,一个编译单元是一个顶层短语,为了正常工作,typeful PPX 必须将类型环境保持在顶层短语之上,这太大了。