How can I incrementally introduce Format.formatter for output when @ signs are occasionally printed?

The current codebase I am working in generally dumps everything directly to stdout or stderr. I am working to change this by adding Format.formatter parameters everywhere, for better control over output. Unfortunately, the Format library treats @ as a special character. Some of the log messages contain the @ symbol, which triggers unwanted Format-specific behavior in the output. I don’t think I can safely change all output messages to use %@ instead of @ since some of them still flow through the old stdout/stderr channels. What is the best way to handle this?

Do you have an example of a problematic behavior? Normally using %@ should be perfectly safe.

For a longer explanation, the Format module doesn’t really treat @ as a special character by itself, for instance

Format.print_string "@."; Format.print_flush ();;

will print

@.

This is because character @ is interpreter at an earlier level in format strings, which are the string-like literal that appears as argument of Printf and Format function. For instance, in

Printf.printf "@."

the format string is "@.". However, this is not really a string but a syntactic sugar for

CamlinternalFormatBasics.(
  Format (Formatting_lit (Flush_newline, End_of_format), "@.")
)

(which you can check with

let _ : _ format6 = "@."

)
Then the Format and Printf printing functions have different interpretations of the
Flush_newline formatting instruction: Format flushes its printing queue and output a newline whereas Printf prints @. to emulate the source. However, this can also create issues if you share format strings between the Printf and Format path, which feels unlikely to happen by accident.

Moreover, both Format and Printf has the same interpretation for %@ (but not @@) and prints the same contents for

Format.printf "%@%!"

and

Printf.printf "%@%!"

Finally, note that you can use Printf.fprintf function too, which takes an Out_channel.t as an argument but otherwise are equivalent to Printf.printf.

2 Likes

Thanks! I switched to using Format.fprintf and that fixed my issues. Actually it was Format.pp_print_text that was giving me trouble; Format.pp_print_text out “\n\n%!” was doing something different from Printf.printf “\n\n%!”. Switching to Format.fprintf was the key.

Ah yes, the argument of pp_print_text is a string, not a format string; thus it does not interpret the Printf/Format specifiers like %!.