Search code examples
ocaml

OCaml combine fprintf and sprintf


I want to write a function that can either print something to a string or to an out_channel. I'm stuck with the internal format stuff however.

My approach is something like this:

type pr_target = Channel of out_channel | StrRef of string ref
let my_print target fmt = match target with
  | Channel ch -> fprintf ch fmt
  | StrRef str -> str := !str ^ sprintf fmt

This gives a type errors as the two uses of fmt are different: fprintf takes an ('a, out_channel, unit) format while sprintf takes an ('a, unit, string) format.

How can I write such a function? I'm not super proficient with modules or OCamls polymorphism.


Solution

  • Format.formatter can be use either buffers or out_channel as formatting backend:

    let b = Buffer.create 10
    let bfmt = Format.formatter_of_buffer b
    let ch = Out_channel.open_text "filename"
    let cfmt = Format.formatter_of_out_channel ch
    let either fmt = Format.fprintf (if Random.bool () then bfmt else cfmt) fmt
    

    Moreover, in general, you want to use asprintf rather than sprintf in order to be able to use %a and %t specifier:

    let s =
      Format.asprintf "@[<v>This is a list [%a]@]"
        Format.(pp_print_list pp_print_string) ["one"; "two"]