-
Notifications
You must be signed in to change notification settings - Fork 0
Description
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.