我正在写我的第一个ppx扩展。其思想是支持多态print函数,类似于Haskell中的show。
(我知道还有其他更健壮的解决方案,但我希望了解更多关于这一点的知识。)
我所采用的方法非常类似于描述这里的方法:我有一个映射器,它查找%[print <expr>]标记,然后用<expr>的字符串表示来替换它们。例如,
[%print 1] ==> string_of_int 1
[%print "aksljd"] ==> "aksljd"对于常量表达式来说,这很好,但是我想支持任何替代<expr>的任意表达式。它应该用最后类型的打印机包装它们。
我目前的方法是使用Typecore.type_expression将Parsetree.expression转换为Typedtree.expression,然后在Typedtree.expression的exp_type字段上进行匹配,并确定用什么替换整个表达式。例如,对于type test = A of int | B of string类型,我会将[%print A 1]替换为show_test (A 1) (show_test必须以约定形式出现)。
这不起作用,因为Typecore.type_expression接受类型环境作为参数,而且在重写时无法得到“当前类型环境”,因为类型检查还没有执行.[%print 1 + 1]和Typecore.type_expression Env.empty会导致Unbound value +,这是它应该做的。
有人有办法解决这个问题吗?如果我完全走错了方向,你可以指出这一点。:p
发布于 2015-08-24 04:22:00
注释中的讨论摘要: Haskell中的show不像这样工作(也不能)。Haskell中的Show a =>将被转换为OCaml中的一个显式模块参数。该模块将具有一个具有类似print : a -> string的签名的值。Haskell通过找到您(或库)声明的Show用于a的实例来推断这个模块参数,但是在OCaml中,您必须手动传递该模块。在Haskell中声明Show实例类似于在OCaml中实例化函子。
在Haskell和OCaml中,诸如问题中的预处理表达式在以下情况下都有可疑的意义:
let f x = [%print x]如果f : 'a -> string (即没有Haskell类型的类实例可用,并且没有传递OCaml模块)。
为了使它更完整,许多分析类型的其他ppx重写程序都是从类型定义或声明而不是从表达式生成代码。
发布于 2015-08-24 03:36:04
您想要做的事情基本上不支持ppx。正如您注意到的,ppx是在解析之后,类型检查尚未完成。由于编译器的当前体系结构,很难键入check任意表达式。
如果你想破解你的方式,你可以读这。)作者也将在ocaml研讨会上做一次关于这一问题的介绍,应该有一段视频。
发布于 2015-08-25 03:08:43
人们已经在某种程度上回答了。我希望我的回答能补充一些东西。
PPX是对非类型化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是非类型化的,但它并不局限于内部的非类型化。你想打什么就打什么。一个典型的例子是将非类型化的输入提供给OCaml类型检查器并获得类型化的AST,然后将获得的输入信息用于您的目的。这种类型PPX所需的工具已经掌握在我们手中:ppx_tools用于通用PPX工具,compiler-libs用于通过OCaml类型检查器和预处理输入,以及untypeast.ml将结果返回到非类型化的AST。链接(https://bitbucket.org/camlspotter/compiler-libs-hack) @Drup已经指出了如何使用它们。它是在PPX发布之前编写的,但是在PPX中,您所做的几乎是一样的,我还在文章中添加了一个关于PPX的小部分。
类型PPX的一个缺点是它不能与REPL一起使用。PPX逐个处理编译单元,并且每个单元处理之间的传输信息是有限的。在REPL中,编译单元是一个toplevel短语,要正确地工作,键入PPX必须在toplevel短语之上保持类型环境,这太大了。
https://stackoverflow.com/questions/32173627
复制相似问题