# Obtaining the necessary type for format strings + arguments

**URL:** <https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070>\
**Category:** Learning\
**Tags:** pretty-printing, printf\
**Created:** [January 10, 2021, 3:40pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070 "2021-01-10T15:40:37Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![cemerick](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/cemerick/32/1383_2.png) [@cemerick](https://discuss.ocaml.org/u/cemerick)\
**Post date:** [January 10, 2021, 3:40pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/1 "2021-01-10T15:40:37Z")

</div>

I have need of a way to print using any existing printer, with the output stripped of all cuts, linebreaks, and indents (think of presenting messages containing probably-small bits of pretty-printed data to users in confined UI spaces).

I landed on this pattern:

```ocaml
let plain fmt =
  let open Format in
  let b = Buffer.create 64 in
  let ppf = formatter_of_buffer b in
  let fns = pp_get_formatter_out_functions ppf () in
  pp_set_formatter_out_functions ppf {fns with out_newline = ignore; out_indent = ignore};
  fprintf ppf "%t" fmt;
  pp_print_flush ppf ();
  Buffer.contents b

let str = plain @@ Format.dprintf "foo%a" (Iter.pp_seq Int.pp) Int.(0 -- 100)
....

```

This certainly gets the job done, but I would very much like to have a single function to call as with `asprintf`. Unfortunately, whatever I try seems to get in the way of the compiler producing the correct type for the format string and its args:

```auto
let p2 x = plain @@ Format.dprintf x
let _ = print_endline @@ p2 "%d" 5

Error: This function has type
         (Format.formatter -> unit, Format.formatter, unit,
          Format.formatter -> unit)
         format4 -> string
       It is applied to too many arguments; maybe you forgot a `;'.

```

I suspect that this is not possible given (my understanding) that the return type of the proximate printing function (`p2` here) is what the compiler keys on, but I thought I’d be hopeful and see if there’s any way to get a tidy `asprintf`-like function with the custom behaviour described above.

---

<div class="post-metadata">

**Author:** ![jjb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/jjb/32/2713_2.png) [@jjb](https://discuss.ocaml.org/u/jjb)\
**Post date:** [January 10, 2021, 4:07pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/2 "2021-01-10T16:07:54Z")

</div>

Maybe `kfprintf` can do enough for your case. What about:

```auto
let plain fmt =
  let open Format in
  let b = Buffer.create 64 in
  let ppf = formatter_of_buffer b in
  let fns = pp_get_formatter_out_functions ppf () in
  pp_set_formatter_out_functions ppf
    {fns with out_newline= ignore; out_indent= ignore} ;
  kfprintf
    (fun ppf ->
      pp_print_flush ppf () ;
      Buffer.contents b )
    ppf fmt

let str = plain "foo%a" (Iter.pp_seq Int.pp) Int.(0 -- 100)
let boo = plain "boo%i" 42

```

---

<div class="post-metadata">

**Author:** ![CraigFe](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/craigfe/32/1989_2.png) [@CraigFe](https://discuss.ocaml.org/u/CraigFe)\
**Post date:** [January 10, 2021, 4:08pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/3 "2021-01-10T16:08:12Z")

</div>

Hi @cemerick.

I suspect what you want is the [`kdprintf`](https://docs.mirage.io/ocaml/Stdlib/Format/index.html#val-kdprintf) function, which is similar to your `dprintf` approach but takes the pieces in the right order to be properly polymorphic in format strings.

```ocaml
let plainer : 'a. ('a, Format.formatter, unit, string) format4 -> 'a =
  fun fmt -> Format.kdprintf plain fmt

```

This works in the way you expect:

```ocaml
# let () = print_endline @@ plainer "%d" 5;;
5

```

You’ll need OCaml 4.08 to have access to `kdprintf`, however.

---

<div class="post-metadata">

**Author:** ![cemerick](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/cemerick/32/1383_2.png) [@cemerick](https://discuss.ocaml.org/u/cemerick)\
**Post date:** [January 11, 2021, 4:48pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/4 "2021-01-11T16:48:36Z")

</div>

That’s great Craig, much appreciated. @jjb’s tweak also works, but it seems like having the `plain` “combinator” around might be useful otherwise in the future.

\<rant\>  
I was an inch or two away from using `kdprintf` like this, but I seem continually confused by both the signatures and documentation of the various `Xprintf` functions…something I used to feel badly about, but there are too many cases where `Xprintf` types just don’t make sense. For example, relevant to `kdprintf` (and somewhat `dprintf`):

1. the signature of `kdprintf` is `((formatter -> unit) -> 'a) -> ('b, formatter, unit, 'a) format4 -> 'b`, but those type parameters overstate the function’s flexibility: `'a` and `'b` must be the same type, which the compiler _will_ figure out (`let plainer fmt = Format.kdprintf plain fmt` is a sufficient definition, the existential type turns out to be superfluous). Presumably there is a good reason why there is more than one type parameter in that signature, but it’s not clear to me.
2. If you give `plainer` an explicit grounded type:

```auto
let plainer_full : (string, Format.formatter, unit, string) format4 -> string =
  fun fmt -> Format.kdprintf asprintf_fmt fmt

```

The function compiles without complaint, but format strings will not be inferred:

```nohighlight
Error: This function has type
     (string, Format.formatter, unit, string) format4 -> string
   It is applied to too many arguments; maybe you forgot a `;'.

```

3. You can provide a completely incoherent combination of types and “continuation” functions, which the compiler will happily oblige:

```auto
let plainer_int fmt : int = Format.kdprintf plain fmt

```

No type error, even though `kdprintf`'s result depends entirely on the “continuation” function (`plain` in this case, returning a string).

Some of this might be a consequence of the very special treatment that the compiler provides for `format*` types, and maybe some is due to legacy of how `Format` has grown over the years, but the net result is consistently confusing anytime I wander off of well-trodden paths.

FWIW, at least the last of the above issues could be addressed if e.g. `kdprintf`'s signature were tightened to reflect its dependence on the provided continuation function:

```auto
module FFormat : sig
  open Format
  val kdprintf :
    ((formatter -> unit) -> 'a) ->
    ('a, formatter, unit, 'a) format4 -> 'a
end = struct
  include Format
end

```

With this, incoherent types are helpfully rejected:

```auto
let plainer_int fmt : int = FFormat.kdprintf plain fmt
Error: This expression has type string but an expression was expected of type int

```

But alas, format strings are no longer inferred properly:

```auto
let plainer2 fmt = FFormat.kdprintf plain fmt
let s = plainer2 "%d" 5
Error: This function has type
       (string, Format.formatter, unit, string) format4 -> string
       It is applied to too many arguments; maybe you forgot a `;'.

```

🙃

I will probably eventually dig into Gabriel Scherer’s [post](http://gallium.inria.fr/blog/format6/) on the format types and how format string inference works, but I don’t think in this case understanding how and why things work as they do will make practical non-garden-path usage of these functions any easier.  
\</rant\>

---

<div class="post-metadata">

**Author:** ![Drup](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/drup/32/35_2.png) [@Drup](https://discuss.ocaml.org/u/Drup)\
**Post date:** [January 11, 2021, 5:36pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/5 "2021-01-11T17:36:30Z")

</div>

So, there are lot’s of things to untangle here. In general, I agree, the documentation is not right. The fundamental reason is that as OCaml API designers, we are used to rely on the types to explain how a function might be used, and the documentation only has to explain what it does.  
As your rightfully point out, for the `Xprintf` family of function, the types are utterly useless at explaining how they can be used.

Here, the important piece you are missing is that `'b` and `'a` play very different roles in `('b, formatter, unit, 'a) format4`.  
`'a` and `'b` are equal If and only If you use a formatting string with exactly 0 hole. Otherwise, they are different.  
In general,

- `'a` is “the return type”. It contains the type returned by `Xprintf` after application of all the arguments. That’s why it’s set to `string` for `sprintf`, and `unit` for `fprintf`.
- `'b` is “the arrow type”. It is of the form `foo -> bar -> baz ..... -> 'a`. It should never be constrained by the `Xprintf` function, as that would limit the accepted format strings.

Unlike what you said in point 1), `'a` and `'b` can (and generally are) different when using `kdprintf`. For instance:

```auto
let f fmt = Format.kdprintf (fun f -> f Format.std_formatter) fmt ;;
val f : ('a, Format.formatter, unit, unit) format4 -> 'a
let s : _ format4 = "Foo %s %d bar" ;;
val s : (string -> int -> 'a, 'b, 'c, 'a) format4
# f s "hello" 3

```

Your “ground” type in 2. is wrong, as it prevents any uses of `%X` in the format string. Same for the simplified type in your `FFormat` module.

Finally, you can build valid cases where the the return type is an int, so it can’t be forbidden “à priori”. For instance a function that returns the size of the printed stuff:

```auto
let print_and_size = let p1 = position_in_chan () in Format.kfprintf (fun _ -> let p2 = position_in_chat () in p2 - p1) my_chan ;;
val print_and_size : ('a, Format.formatter, unit, int) format4 -> 'a

```

While you might not need to understand “how format string inference works”, it’s useful to have an idea of what those parameters mean, which is exactly what Gache’s blog post explains.

In general, it’s difficult to really change/improve the API without preventing valid and practical usage of format. The interplay of the various parameters is indeed a bit complex, but has been proven to lend itself to lot’s of nice uses.  
What is possible, and that we should definitely try to do, is to improve the documentation to make it more accessible, and less of “ask Octachron/JJB/one of the gabriels for magic runes on discuss”.

---

<div class="post-metadata">

**Author:** ![cemerick](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/cemerick/32/1383_2.png) [@cemerick](https://discuss.ocaml.org/u/cemerick)\
**Post date:** [January 11, 2021, 5:44pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/6 "2021-01-11T17:44:29Z")

</div>

> [@Drup](#):
>
> Your “ground” type in 2. is wrong, as it prevents any uses of `%X` in the format string, as does the simplified type in your `FFormat` module.

These sorts of topics always make me feel like I’m surrounded by trapdoors where I can be proven to understand nothing. 🙂

Given the existential type `plainer : 'a. ('a, Format.formatter, unit, string) format4 -> 'a`, where the concrete instance clearly returns `string`, how is the ground type not `(string, formatter, unit, string) format4 -> string`? How would anything else unify?

---

<div class="post-metadata">

**Author:** ![Drup](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/drup/32/35_2.png) [@Drup](https://discuss.ocaml.org/u/Drup)\
**Post date:** [January 11, 2021, 5:45pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/7 "2021-01-11T17:45:35Z")

</div>

> [@cemerick](#):
>
> These sorts of topics always make me feel like I’m surrounded by trapdoors where I can be proven to understand nothing.

In this case, the fault does not lie in your understanding, but in the (lack of) documentation. 😉

---

<div class="post-metadata">

**Author:** ![Drup](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/drup/32/35_2.png) [@Drup](https://discuss.ocaml.org/u/Drup)\
**Post date:** [January 11, 2021, 5:49pm UTC](https://discuss.ocaml.org/t/obtaining-the-necessary-type-for-format-strings-arguments/7070/8 "2021-01-11T17:49:27Z")

</div>

> [@cemerick](#):
>
> Given the existential type `plainer : 'a. ('a, Format.formatter, unit, string) format4 -> 'a` , where the concrete instance clearly returns `string` , how is the ground type not `(string, formatter, unit, string) format4 -> string` ? How would anything else unify?

When used in a type signature, this syntax is a universal (i.e., it forces a function to be fully polymorphic), not an existential. In this particular case, you can just remove it. Inference already works well.
