如何在相互递归类型中使用派生 Show()?

How to use deriving Show() in mutually recursive types?

直接看代码

type symbol =
   | JumpDes of int 
   | CallDes of func
   | StarDes of exp (*here needs the definition of type exp*)
  deriving (Show)
type exp =
   | Const of const
   | Symbol of symbol (*here needs the definition of type symbol*)
   | Reg of reg
   | Assist of assistop
   | Ptr of ptraddr
   | Label of string
   deriving (Show)

我想使用包 deriving to print info about the two types. According to its document,我只需要在要打印的类型后添加 deriving (Show)。但是我不能通过像这样添加 and 来定义相互递归类型:

type symbol =
   | JumpDes of int
   | CallDes of func
   | StarDes of exp 
  deriving (Show)
and exp = (*note that it's line 240*)
   | Const of const
   | Symbol of symbol
   | Reg of reg
   | Assist of assistop 
   | Ptr of ptraddr
   | Label of string
   deriving (Show)

编译上面的代码会出现以下错误:

File "type.ml", line 240, characters 16-17:
Parse error: [semi] expected after [str_item] (in [implem])
File "type.ml", line 1:
Error: Error while running external preprocessor

如果我想在相互递归类型上使用派生 Show() 应该怎么办?感谢您的帮助!

要导出 pretty-printing 函数,您可以使用 ppx_deriving1。首先,确保你已经安装了它,

opam install ppx_deriving

接下来,让我们根据您的输入创建一个示例项目,

type func = string [@@deriving show]
type const = int [@@deriving show]
type reg = int [@@deriving show]
type assistop = int [@@deriving show]
type ptraddr = int [@@deriving show]

type symbol =
  | JumpDes of int
  | CallDes of func
  | StarDes of exp
[@@deriving show]

and exp =
  | Const of const
  | Symbol of symbol
  | Reg of reg
  | Assist of assistop
  | Ptr of ptraddr
  | Label of string
[@@deriving show]


let () =
  let input = StarDes (Const 1) in
  Format.printf "%s\n%a@\n" (show_symbol input) pp_symbol input

我为您在问题中未提供的类型添加了一些别名。请注意,所有类型都必须指定 [@@deriving show]。当一个类型是递归的时候,只需将 and 视为 type。基本上,和你做的一样,但我们需要使用 ppx-syntax,例如 [@@deriving show] 而不是 Ocsigen 的

在程序的最后,有一个例子展示了如何使用生成的函数。有两种类型的函数,show_foo 将类型 foo 的值转换为字符串,pretty-printing 函数 pp_foofoo 打印到格式化程序中,速度更快,因为不需要创建中间字符串。打印机与 %a 说明符一起使用并带有两个参数,即打印机本身和要打印的值。所以不用加括号,也是小胜。我在示例中使用了两个选项,以便您对比它们。

最后,如何搭建呢?如果您使用 dune,那么这里是示例 dune 文件,

(executable
 (name example)
 (preprocess
  (pps ppx_deriving.show)))

可以自己创建dune文件或者使用下面的命令(假设我们的程序在example.ml文件中),

dune init executable example --ppx=ppx_deriving.show

您可以 运行 例如,

dune exec ./example.exe

如果您使用 ocamlbuild 而不是 dune,则只需将 -package ppx_deriving.show 添加到您的 ocamlbuild 调用中,例如,

ocamlbuild -package ppx_deriving.show example.native
./example.native

两者都会打印,

(Example.StarDes (Example.Const 1))
(Example.StarDes (Example.Const 1))

如果您正在使用其他构建系统,请不要犹豫,我们将需要有关您的构建系统的更多信息或 link 您的项目。如果您刚刚开始一个新项目并且不知道应该使用哪个构建系统,那么沙丘就是答案。


1)这不是唯一的选择。您还可以使用 ppx_jane 的 sexp 派生器 ppx_show,可能还有更多。