# Printf / ANSITerminal pain points

**URL:** <https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498>\
**Category:** Ecosystem\
**Created:** [November 15, 2025, 6:18pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498 "2025-11-15T18:18:09Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [November 15, 2025, 6:18pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/1 "2025-11-15T18:18:09Z")

</div>

Another pain point which hasn’t been mentioned yet (or maybe it fell through the cracks of this rather long thread) is the composition (or rather, lack thereof) of printf functions.

Concrete example:  
The ANSITerminal exports

- a `val printf : style list -> ('a, unit, string, unit) format4 -> 'a` function
- a `eprintf` variant with the same type
- a `val sprintf : style list -> ('a, unit, string) format -> 'a` function

First, the format4, format, format6 types are arcane because of the positional type parameters. There could be a single object type to hold the type parameters and it’d give names to them which would be more readable.

Second, if you want to print into a buffer well you can’t do that directly (or can you? I’m not 100% because it’s such a complex piece of the stdlib), you have to go through the string and then add that stirng to the buffer. You have to make the code more complex and less efficient.

Now one could patch ANSITerminal to have a `bprintf` variant. And then also a `ifprintf` and `ibprintf` and a plethora of others. But it’s a shame that libraries have to export half a dozen functions for essentially the same functionality over slightly different data types.

I think there’s a good opportunity for language/stdlib design there.

---

<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:** [November 15, 2025, 8:40pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/2 "2025-11-15T20:40:06Z")

</div>

If ANSITerminal had provided `fprintf`, even instead of the other 3, then it could be used to print to stdout (using `Format.std_formatter`), stderr (using `Format.err_formatter`), a buffer of your choice (using `Format.formatter_of_buffer`), a string (using `Format.str_formatter`), etc. I think this is more a case of a library exposing the convenience wrappers but not the general form of an operation, rather than an issue with the standard library’s expressiveness.

But yes, `format6` is arcane. Perhaps an object type would make it more understandable. Sounds like something to trial with a small wrapper library of `Format` adding and using such a type alias.

---

<div class="post-metadata">

**Author:** ![WardBrian](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/wardbrian/32/3971_2.png) [@WardBrian](https://discuss.ocaml.org/u/WardBrian)\
**Post date:** [November 15, 2025, 9:05pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/4 "2025-11-15T21:05:40Z")

</div>

> [@raphael-proust](#):
>
> I think there’s a good opportunity for language/stdlib design there.

I’m rather excited for the l\*printf functions ([New `Format` and `Printf` `printf`-like functions that accept a heterogeneous list as arguments by zazedd · Pull Request #13372 · ocaml/ocaml · GitHub](https://github.com/ocaml/ocaml/pull/13372)), which appear to make composing print functions much easier, without the need for continuation based code

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [November 15, 2025, 10:16pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/5 "2025-11-15T22:16:16Z")

</div>

oh that is a welcome addition! thanks for the pointer

---

<div class="post-metadata">

**Author:** ![c-cube](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/c-cube/32/1727_2.png) [@c-cube](https://discuss.ocaml.org/u/c-cube)\
**Post date:** [November 15, 2025, 10:34pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/6 "2025-11-15T22:34:26Z")

</div>

The real answer is, and always has been imho, that people ought to be able to build their own out\_channels. This way we’d only need fprintf because bprintf would just be fprintf into a buffer-backed out\_channel. This is really a place where the stdlib lacks extensibility.

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [November 16, 2025, 11:47am UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/7 "2025-11-16T11:47:24Z")

</div>

> [@jjb](#):
>
> If ANSITerminal had provided `fprintf`

I don’t see how you can make a `buffer` into an `out_channel` so you definitely need `Format.fprintf` rather than `Printf.fprintf`. But when you print out terminal control characters they shouldn’t count towards the length of the pretty-printing box thingy features of `Format`. The simplest thing (the thing that’s not huge amount of code) is to only expose `Printf`-like extrapolation strings.

Oh and also surely you mean `kfprintf` because with `fprintf` alone you can’t do something like

```auto
let warn m =
  kfprintf
    (fun _ -> some other code here)
    style
    msg

```

oh and `ikfprintf` as well! (although I guess you could get away with passing a devnull fmt but you’d still pay execution time though)

So I don’t think “just expose `Format.fprintf`-compatible functions” is a workable solution.

---

<div class="post-metadata">

**Author:** ![JohnJ](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/johnj/32/6020_2.png) [@JohnJ](https://discuss.ocaml.org/u/JohnJ)\
**Post date:** [November 16, 2025, 3:20pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/8 "2025-11-16T15:20:34Z")

</div>

These kind of feel more like pain points of ANSITerminal specifically. Basing it on Format instead of Printf would be more extensible, and control characters don’t have to count towards the box length, since Format allows you to control the length of any printed string (e.g. as 0). Even without using Format, the stdlib’s Printf includes functions like bprintf, which I assume ANSITerminal could expose too. (Although I say this as someone who hasn’t used ANSITerminal or much less contributed to it, so it may be more complicated than that.)

I do agree with the more general pain point that the ecosystem’s status-quo makes composable APIs much harder to write, though, which leads to situations like this. A better design around channels (like letting us build our own) would give users a lot of these desired benefits for “free.”

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [November 16, 2025, 7:30pm UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/9 "2025-11-16T19:30:08Z")

</div>

> [@JohnJ](#):
>
> These kind of feel more like pain points of ANSITerminal specifically

ANSITerminal was picked randomly as an example.

I also have issues with `CCUnix` where the `call*` functions can’t easily be wrapped/combined because they have the interpolation arguments. You end up having to do normal printfs and then pass the string as a whole or something.

I think the fact that there are two separate modules in the Stdlib as well as two prominent libraries with the single focus of providing good alternatives to the Stdlib (`Fmt` and `Pprint`) goes to show that things could be improved.

---

<div class="post-metadata">

**Author:** ![threepwood](https://avatars.discourse-cdn.com/v4/letter/t/8dc957/32.png) [@threepwood](https://discuss.ocaml.org/u/threepwood)\
**Post date:** [November 17, 2025, 8:44am UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/10 "2025-11-17T08:44:32Z")

</div>

I hit the same issue recently and complained about it [in some other thread](https://discuss.ocaml.org/t/pretty-printing-with-format), where some of the maintainers proved useful explanations in response. Among other things, if a library function that accepts formats doesn’t provide a continuation variant, wrapping it with:

```auto
Format.kdprintf (fun ppf -> do_thing @@ the_library_function "%t" ppf)

```

seems to be blessed way, and the soon-to-come heterogeneous list approach to format arguments will allow you to just write it in the natural way.

---

<div class="post-metadata">

**Author:** ![raphael-proust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/raphael-proust/32/415_2.png) [@raphael-proust](https://discuss.ocaml.org/u/raphael-proust)\
**Post date:** [November 17, 2025, 9:25am UTC](https://discuss.ocaml.org/t/printf-ansiterminal-pain-points/17498/11 "2025-11-17T09:25:52Z")

</div>

could be nice to define a `let@` to wrap around the continuation

```auto
let (let@) x f = f x
let f x y =
  let@ s = Format.ksprintf Fun.id "%d%d" x y in
  String.length s

```

I’m sure similar variants can work for different `k*printf` functions
