Search code examples
ocamlderivingocsigen

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


Let's directly see the codes.

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)

I'd like to use package deriving to print info about the two types. According to its document, i just need to add deriving (Show) after types i want to print. But i can not define mutually recursive types by adding and like this:

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)

Compiling codes above will get error below:

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

How should i do if i'd like to use deriving Show() on mutually recursive types? Thanks for your help!


Solution

  • To derive pretty-printing functions you can use ppx_deriving1. First of all, make sure that you have it installed,

    opam install ppx_deriving
    

    Next, let's create a sample project based on your input,

    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
    

    I put some aliases for the types that you didn't provide in your question. Notice that all the types had to specify [@@deriving show]. When a type is recursive, just treat and as type. Basically, the same as you did, but we need to use the ppx-syntax, e.g., [@@deriving show] not the Ocsigen one.

    At the end of the program, there is an example that shows how to use the generated functions. There are two types of function, show_foo which translates a value of type foo into the string, and the pretty-printing function pp_foo that prints foo into the formatter, which is faster since there is no need to create an intermediate string. The printers are used with the %a specifier and take two arguments, the printer itself and the value to print. So there's no need to put parentheses, also a small win. I used both options in the example, so that you can contrast them.

    Finally, how to build it? If you're using dune, then here's the sample dune file,

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

    You can create the dune file yourself or use the following command (assuming that our program is in the example.ml file),

    dune init executable example --ppx=ppx_deriving.show
    

    You can run example with,

    dune exec ./example.exe
    

    If you're using ocamlbuild instead of dune, then just add -package ppx_deriving.show to your ocamlbuild invocation, e.g.,

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

    Both will print,

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

    If you're using some other build system, then don't hesitate to ask, we will need more information about your build system or a link to your project. If you just starting a new project and don't know which build system you should use, then dune is the answer.


    1)This is not the only option. You can also use ppx_jane's sexp deriver, ppx_show, and probably there are a few more.