# \[ANN\] ppx\_format

**URL:** <https://discuss.ocaml.org/t/ann-ppx-format/17211>\
**Category:** Ecosystem\
**Created:** [September 5, 2025, 2:51pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211 "2025-09-05T14:51:09Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)\
**Post date:** [September 5, 2025, 2:51pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/1 "2025-09-05T14:51:09Z")

</div>

I happy to announce the first release of [ppx\_format](https://github.com/EmileTrotignon/ppx_format).

Its a small ppx rewriter that was first written at the mirage retreat in 24 with @PizieDust , and that allows to put values in the middle of format strings:

```ocaml
let s = "World"
let x = 123
let () = Format.printf {%i|Hello {%s s} {%a Format.pp_print_char % Char.chr 65} {%d x}%!|}

```

Its compatible with any function that takes format strings. The only constraint is that the format string has to be the last argument.

I have used at in some of my projects, and it will be available on opam as soon as the release PR is merged.

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [September 5, 2025, 5:50pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/2 "2025-09-05T17:50:18Z")

</div>

Would it be possible to make the syntax like this: `Format.printf {|Hello %s{s}|}`?

---

<div class="post-metadata">

**Author:** ![gasche](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/gasche/32/4_2.png) [@gasche](https://discuss.ocaml.org/u/gasche)\
**Post date:** [September 6, 2025, 4:27pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/3 "2025-09-06T16:27:33Z")

</div>

Fun coincidence: at the last Mirage retreat I discussed format interpolation with @octachron, and I wrote the beginning of a RFC for it (and then promptly forgot about it). I just pushed it at […/format-interpolation.md](https://github.com/gasche/RFCs/blob/format-interpolation/rfcs/format-interpolation.md).

---

<div class="post-metadata">

**Author:** ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)\
**Post date:** [September 6, 2025, 4:41pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/4 "2025-09-06T16:41:39Z")

</div>

You might consider a different interpolation syntax (viz. [GitHub - camlp5/pa\_ppx\_fmtformat: A PPX rewriter to provide string-interpolation, using Fmt as the underlying mechanism](https://github.com/camlp5/pa_ppx_fmtformat?tab=readme-ov-file#interpolated-expressions) )

The advantage of the suggested format for interpolated expressions over “%{…%}” is that with the multiple forms, there’s never a need to escape – you can just pick a different form.

====

The simplest interpolated expression is of the form `$(...)` but all of the following are accepted:

- `$(...)`, `$(|...|)`
- `$[...]`, `$[|...|]`
- `${...}`, `${|...|}`
- `$<...>`, `$<|...|>`

So basically, ‘$’ followed by any of [‘(’, ‘[’, ‘{’, ‘\<’],  
optionally ‘|’, and then at the end, the matching text. Between these  
8 forms, it should be possible to enclose any interpolated expression  
without difficulty, I would think.

In the text surrounded by these delimiter, anything other than the  
end-string is acceptable, and there is no provision made for escaping.

The contents of the interpolated expression can be of three forms:

==== interpolated expression with format-specifier: `$( <expression> | <format-specifier> )`

an interpolated expression of the form `$(abc|%d)` specifies that the  
expression `abc` will be formatted with `%d`. So `{%fmt_str|a $(abc|%d)|}` expands to  
`Fmt.(str "a %d" abc)`.

==== interpolated expression with `Fmt` formatter: `$( <expression> | <Fmt formatter expression> )`

an interpolated expression of the form `$(abc|int)` specifies that the  
expression `abc` will be formatted with the Fmt formatter `int`. So `{%fmt_str|a $(abc|int)|}` expands to  
`Fmt.(str "a %a" int abc)`.

==== interpolated expression without specifier/formatter: `$( <expression> )`

an interpolated expression of the form `$(abc)` specifies that the  
expression `abc` will be formatted with `%s`. So `{%fmt_str|a $(abc)|}` expands to  
`Fmt.(str "a %s" abc)`.

---

<div class="post-metadata">

**Author:** ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)\
**Post date:** [September 6, 2025, 7:10pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/5 "2025-09-06T19:10:38Z")

</div>

Implementation wise it should be pretty easy. However I use it quite a bit in other project so I would prefer to not change the syntax. I think if you really want it this it should be easy to maintain a fork

---

<div class="post-metadata">

**Author:** ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)\
**Post date:** [September 6, 2025, 7:11pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/6 "2025-09-06T19:11:33Z")

</div>

Interesting indeed. On the implementation side, my version basically has a second lexer.

---

<div class="post-metadata">

**Author:** ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)\
**Post date:** [September 6, 2025, 7:18pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/7 "2025-09-06T19:18:09Z")

</div>

> [@Chet\_Murthy](#):
>
> In the text surrounded by these delimiter, anything other than the  
> end-string is acceptable, and there is no provision made for escaping.

I dont have provision for escaping either. My idea is that if you want something like “{%` in your expression, you can always bind that expression to a variable and use the variable. I don’t think its very nice to have very complex expressions inside the format string.

In my opinion, using `{%` in the format string is more of an issue than using it in the expressions, and its not solved by your proposed syntax.

Also the idea to have special syntax for `%a` that does not have `%a` in it might be good, but my goal with this ppx was to provide maximum familiarity: someone used by printf/format can read code written with my ppx and immediately guess what is happening correctly.

---

<div class="post-metadata">

**Author:** ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)\
**Post date:** [September 7, 2025, 4:29am UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/8 "2025-09-07T04:29:38Z")

</div>

Emile,

First, your rationale is excellent, and I understand your reasoning. I cannot fault it. I would add, though, that when one can start using the equivalent of [from Perl5]

```auto
"abc $foo def $bar"

```

in OCaml, you start using more and more of the stuff, assembling more and more complicated strings containing interpolated variables. And then you get to where you want an interpolated -expression- …

```auto
"abc ${\( f($foo) )} def $bar"

```

and you’re off to the races. That idiom `${\( …)}` is a “scalar context” within a string – you can put any scalar expression in there. And at that point, you can write rather complex structural expressions, that are all one big printf.

It was when I realized I could do this in Perl5 back in 1995, that I became a Perl bigot.

All of this is way to try to express that while surely Printf-style format-strings do not encourage complexity and depth in the expressions one writes, when you can start doing interpolation, and esp. interpolation with -expressions-, at least some people will find it irressistible.

And I do think that it’s more readable to do things this way, than to use loops/iterators and such to write the same thing without interpolation.

All that said, for sure I understand your position, and given your design goal, I can’t dispute your decision.

---

<div class="post-metadata">

**Author:** ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)\
**Post date:** [September 7, 2025, 12:41pm UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/9 "2025-09-07T12:41:47Z")

</div>

> [@Chet\_Murthy](#):
>
> And at that point, you can write rather complex structural expressions, that are all one big printf

I think thats probably quite good, but in OCaml, you get degraded tooling inside the format string: merlin is spotty, there is no ocamlformat, no syntax highlighting. So in my opinion its better to outsource complex logic to a variable above. If such expressions-in-strings were a first-class citizen of language, like I am guessing they are in perl, it would be quite different.

---

<div class="post-metadata">

**Author:** ![PizieDust](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/piziedust/32/5269_2.png) [@PizieDust](https://discuss.ocaml.org/u/PizieDust)\
**Post date:** [March 9, 2026, 8:03am UTC](https://discuss.ocaml.org/t/ann-ppx-format/17211/10 "2026-03-09T08:03:11Z")

</div>

😄 it was great watching you build this
