Module 使用多态递归模块创建访问者模式

Module 使用多态递归模块创建访问者模式,module,polymorphism,ocaml,functor,visitor,Module,Polymorphism,Ocaml,Functor,Visitor,(免责声明:我相当肯定这在任何方面都不是惯用语。如果OCaml中有其他树遍历惯用语,我洗耳恭听:) 我正在用OCaml编写一个玩具编译器,我希望有一个访问者访问我的大型语法树类型。我使用类编写了一个,但我认为尝试使用模块/函子实现一个会很有趣。我的类型层次结构非常庞大,所以让我来说明我正在尝试做什么 考虑以下类型定义(即席制作): 让我简要说明一下用法。比如说,我想收集程序中所有的SVarRefs。如果我有一个映射访问者(它访问树的每个节点,默认情况下什么都不做),我可以做以下事情(在一个完美的

(免责声明:我相当肯定这在任何方面都不是惯用语。如果OCaml中有其他树遍历惯用语,我洗耳恭听:)

我正在用OCaml编写一个玩具编译器,我希望有一个访问者访问我的大型语法树类型。我使用类编写了一个,但我认为尝试使用模块/函子实现一个会很有趣。我的类型层次结构非常庞大,所以让我来说明我正在尝试做什么

考虑以下类型定义(即席制作):

让我简要说明一下用法。比如说,我想收集程序中所有的
SVarRef
s。如果我有一个映射访问者(它访问树的每个节点,默认情况下什么都不做),我可以做以下事情(在一个完美的世界中):

我应该注意的是,为每个特定变体提供函数的好处是,我的实际代码库有一个类型,它既有大量变体,也没有意义进行分段。特定于变量的函数允许避免对类型的其他变量重复迭代实现

看起来很简单,但这里有一个陷阱:有不同类型的访客
MapVisitor
返回原始语法树,因此它具有类型

sig
  (** Dispatches to variant implementations *)
  val visit_stmt : stmt -> stmt
  val visit_expr : expr -> expr
  (** Variant implementations *)
  val s_num : int -> expr
  val s_var_ref : string -> expr
  val s_add : (expr * expr) -> expr
  val s_do : stmt list -> expr
  val s_if : (expr * expr * expr) -> stmt
  val s_assign : (string * expr) -> stmt
end
同时,可以想象一个折叠访问者,其中每个函数的返回类型都是一些
t
。试图尽可能地抽象这一点,以下是我的尝试:

module type AST_DISPATCHER = sig
  type expr_ret
  type stmt_ret
  val visit_expr : expr -> expr_ret
  val visit_stmt : stmt -> stmt_ret
end
(** Concrete type designation goes in AST_VISITOR_IMPL *)
module type AST_VISITOR_IMPL = sig
  type expr_ret
  type stmt_ret

  val s_num : int -> expr_ret
  val s_var_ref : string -> expr_ret
  val s_add : (expr * expr) -> expr_ret
  val s_do : stmt list -> expr_ret

  val s_if : (expr * expr * expr) -> stmt_ret
  val s_assign : (string * expr) -> stmt_ret
end
module type AST_VISITOR = sig
  include AST_VISITOR_IMPL
  include AST_DISPATCHER with type expr_ret := expr_ret
                          and type stmt_ret := stmt_ret
end

(** Dispatcher Implementation *)
module AstDispatcherF(IM : AST_VISITOR_IMPL) : AST_DISPATCHER = struct
  type expr_ret = IM.expr_ret
  type stmt_ret = IM.stmt_ret

  let visit_expr = function
    | SNum(i) -> IM.s_num i
    | SVarRef(s) -> IM.s_var_ref s
    | SAdd(l,r) -> IM.s_add (l,r)
    | SDo(sl) -> IM.s_do sl

  let visit_stmt = function
    | SIf(c,t,f) -> IM.s_if (c,t,f)
    | SAssign(s,e) -> IM.s_assign (s,e)
end
module rec MapVisitor : AST_VISITOR = struct
  type expr_ret = expr
  type stmt_ret = stmt
  module D : (AST_DISPATCHER with type expr_ret := expr_ret
                              and type stmt_ret := stmt_ret)
    = AstDispatcherF(MapVisitor)

  let visit_expr = D.visit_expr
  let visit_stmt = D.visit_stmt

  let s_num i = SNum i
  let s_var_ref s = SVarRef s
  let s_add (l,r) = SAdd(D.visit_expr l, D.visit_expr r)
  let s_do sl = SDo(List.map D.visit_stmt sl)

  let s_if (c,t,f) = SIf(D.visit_expr c, D.visit_expr t, D.visit_expr f)
  let s_assign (s,e) = SAssign(s, D.visit_expr e)
end
但是,运行此命令会显示以下错误消息:

Error: Signature Mismatch:
       Values do not match:
         val visit_expr : expr -> expr_ret
       is not included in
         val visit_expr : expr -> expr_ret

我知道这意味着我没有正确地表达类型之间的关系,但我无法找出这种情况下的修复方法。

免责声明:模块只是带有类型定义的值的记录。由于您的模块中没有类型,所以根本不需要使用它们,只需使用普通的旧记录类型,您将获得一种惯用的AST遍历模式。很快,您就会发现您需要一个开放的递归,并将切换到基于类的方法。不管怎么说,这就是将类添加到OCaml的主要原因。您还可以将类包装到状态monad中,以使用任意用户数据折叠AST

关于您的错误消息,很简单,您用签名隐藏您的类型,这是一个常见的错误。最简单的解决方案是完全忽略函子返回类型的注释,或者使用类型expr=expr注释传播类型相等

如果您需要更多惯用方法的示例,那么对于您可以查找的记录,下面是一个使用类实现的不同访问者的示例,包括封装到状态monad中的类

module type AST_DISPATCHER = sig
  type expr_ret
  type stmt_ret
  val visit_expr : expr -> expr_ret
  val visit_stmt : stmt -> stmt_ret
end
(** Concrete type designation goes in AST_VISITOR_IMPL *)
module type AST_VISITOR_IMPL = sig
  type expr_ret
  type stmt_ret

  val s_num : int -> expr_ret
  val s_var_ref : string -> expr_ret
  val s_add : (expr * expr) -> expr_ret
  val s_do : stmt list -> expr_ret

  val s_if : (expr * expr * expr) -> stmt_ret
  val s_assign : (string * expr) -> stmt_ret
end
module type AST_VISITOR = sig
  include AST_VISITOR_IMPL
  include AST_DISPATCHER with type expr_ret := expr_ret
                          and type stmt_ret := stmt_ret
end

(** Dispatcher Implementation *)
module AstDispatcherF(IM : AST_VISITOR_IMPL) : AST_DISPATCHER = struct
  type expr_ret = IM.expr_ret
  type stmt_ret = IM.stmt_ret

  let visit_expr = function
    | SNum(i) -> IM.s_num i
    | SVarRef(s) -> IM.s_var_ref s
    | SAdd(l,r) -> IM.s_add (l,r)
    | SDo(sl) -> IM.s_do sl

  let visit_stmt = function
    | SIf(c,t,f) -> IM.s_if (c,t,f)
    | SAssign(s,e) -> IM.s_assign (s,e)
end
module rec MapVisitor : AST_VISITOR = struct
  type expr_ret = expr
  type stmt_ret = stmt
  module D : (AST_DISPATCHER with type expr_ret := expr_ret
                              and type stmt_ret := stmt_ret)
    = AstDispatcherF(MapVisitor)

  let visit_expr = D.visit_expr
  let visit_stmt = D.visit_stmt

  let s_num i = SNum i
  let s_var_ref s = SVarRef s
  let s_add (l,r) = SAdd(D.visit_expr l, D.visit_expr r)
  let s_do sl = SDo(List.map D.visit_stmt sl)

  let s_if (c,t,f) = SIf(D.visit_expr c, D.visit_expr t, D.visit_expr f)
  let s_assign (s,e) = SAssign(s, D.visit_expr e)
end
Error: Signature Mismatch:
       Values do not match:
         val visit_expr : expr -> expr_ret
       is not included in
         val visit_expr : expr -> expr_ret