Skip to content

Printing a document twice with a different renderer function #2

@zbyrn

Description

@zbyrn

The Issue

I found this issue while porting this great library to Standard ML. The following snippet reproduces it:

let print_doc_choice (w : int) =
  let cf = Printer.default_cost_factory ~page_width:w () in
  let module P = Printer.Make (val cf) in
  let open P in

  let d = text "while (true) {" ^^
          nest 4
            (nl ^^ text "f();" ^^ nl ^^ text "if (done())" ^^
             (let exit_d = text "exit();" in
              (space ^^ exit_d) <|> nest 4 (nl ^^ exit_d))) ^^
          nl ^^ text "}"
  in
  print_string (pretty_format d);
  print_string "\n====================\n";
  print_string (pretty_format d)

Output:

utop # print_doc_choice 80;;
while (true) {
    f();
    if (done()) exit();
}
====================
while (true) {
    
}

Although rare in practice, I believe it should be safe for a document to be rendered more than once.

The Cause

In each measure set in the memoization table, the function/thunk layout : unit -> unit captures the provided renderer. However, when one tries to print an already resolved document with a new renderer, the printer retrieves the layout function from the memo table and calls the old renderer.

In the above example, the second call to pretty_format writes to the buffer created for the first call.

Potential Fix

A potential fix is to change to the type of layout to unit -> string or unit -> string list/treeof, which returns the rendered string(s) instead of calling the renderer. However, this method does buffer the entire rendered document before sending it to renderer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions