Ocaml 基本类型驱动的ppx重写

Ocaml 基本类型驱动的ppx重写,ocaml,metaprogramming,Ocaml,Metaprogramming,我正在写我的第一个ppx扩展。其思想是支持多态的print功能,类似于Haskell中的show (我知道还有其他更稳健的解决方案,但我希望了解更多关于这一点的工作原理。) 我采用的方法与描述的方法非常相似:我有一个映射器,它查找%[print]标记,然后用的字符串表示法替换它们。比如说, [%print 1] ==> string_of_int 1 [%print "aksljd"] ==> "aksljd" 这适用于常量表达式,但我希望支持任何任意表达式来代替。它应该用最终类型

我正在写我的第一个ppx扩展。其思想是支持多态的
print
功能,类似于Haskell中的
show

(我知道还有其他更稳健的解决方案,但我希望了解更多关于这一点的工作原理。)

我采用的方法与描述的方法非常相似:我有一个映射器,它查找
%[print]
标记,然后用
的字符串表示法替换它们。比如说,

[%print 1] ==> string_of_int 1
[%print "aksljd"] ==> "aksljd"
这适用于常量表达式,但我希望支持任何任意表达式来代替
。它应该用最终类型的打印机包装它们

我目前的方法是使用
Typecore.type_expression
Parsetree.expression
转换为
Typedtree.expression
,然后匹配
Typedtree.expression
字段,并确定用什么替换整个表达式。例如,对于类型
type test=a of int | B of string
,我会将
[%print a1]
替换为
show_test(a1)
show_test
必须按惯例存在)

这不起作用,因为
Typecore.type\u表达式
将类型环境作为参数,我无法在重写时获取“当前类型环境”,因为那时甚至还没有执行类型检查
[%print 1+1]
使用
Typecore.type\u expression Env.empty
会导致
未绑定值+
,这是应该的


有人能解决这个问题吗?如果我完全朝着错误的方向前进,请随时指出这一点p

ppx基本上不支持您想要做的事情。正如您所注意到的,ppx在解析之后,类型检查还没有完成。 由于当前编译器的体系结构,很难对任意表达式进行类型检查

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

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

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

let f x = [%print x]
如果
f:'a->string
(即没有可用的Haskell类型类实例,也没有传递OCaml模块)


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

人们已经在某种程度上回答了这个问题。。。我希望我的回答能有所补充

PPX是非类型化AST的预处理。它应该用于AST转换,而不知道输入的类型

ppx_派生
和其他一些ppx预处理器自动为每个类型定义和构造函数生成代码。它们确实很酷,但它们完全在非类型层中工作:它们只是利用类型定义的语法结构。对于依赖类型的行为,您需要在非类型化AST:
[%deriver.show:(int*int)列表][(1,2);(3,4);(5,6)]
中明确给出类型信息<代码>[%derivate.show][(1,2);(3,4);(5,6)]不起作用

因此,PPX的I/O是非类型化的,但并不限于在其内部是非类型化的。你可以随心所欲地打字。一个典型的例子是将非类型化的输入提供给OCaml类型检查器并获得一个类型化的AST,然后将获得的类型信息用于您的目的。这种类型的PPX所需的工具已经在我们手中:
PPX\u工具
对于通用PPX工具,
编译器libs
用于OCaml类型检查器的键入和预处理,以及
untypeast.ml
使结果返回到非类型AST。Drup指出的链接()解释了如何使用它们。它是在PPX出现之前写的,但是在PPX中,你所做的是完全一样的,我也在文章中为PPX添加了一个小部分


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

这是什么意思:
let f x=[%print x]
?很好。是的,多态性不适用于此。我还没有把所有的事情都想清楚,这只是学习ppx的第一次天真尝试:)有了这些信息,一个等价的函数
print::Show a=>a->String
如何在Haskell中工作呢?在通过类型检查后,编译器是否将其内联,然后用静态已知的实现替换
show
的每个实例?没问题。从你的问题中我不确定——你是否意识到,出于同样的原因(以及其他原因),这在Haskell也不起作用?Haskell所做的是有效地传递一个隐藏的“模块”(类型类实例),该模块为某些类型实现show。您可以将
show a=>
看作是传递的OCaml一级模块,签名如下
sig type t=a val to_string:a->string end
。当您使用
a
上设置字符串时,Haskell会从该“模块”中选择该字符串