An inconvenient with "%{print_user}{user}a"
is that the format string is no longer serializable easily, which can make it harder to translate them by reading an on-disk translation file for instance.
If we think of template syntax as shuffling the argument application order (I think that the most reasonable semantics is to evaluate the template arguments in-order to values, at the moment the format string itself is evaluated), then I think that "%a"
would be an equivalent format string to serialize.
Edit: I had in mind a rewrite at the type-checking stage, but I think that this cannot work before the format consumer is known, so now I see that this would require adding new information to the format structure; in that case it may be that the equivalence proposed above doesn’t work. More thinking required!
Could this be implemented as a syntactical transformation at the AST level?
"Hello %{user.name}s, your account is %{account_number user}d."
to
Printf.sprintf "Hello %s, your account is %d" user.name (account_number user)
I think this transformation is quite feasible, though it might be reasonable, if types are present, to simply guess that the first, since it is a string, should be interpolated as is, and the second, since it is an integer, should be turned into a string. Then you don’t need the s
and d
at all. Types are great things.
The format conveys more than the type. Consider %5.2f
and %5LX
.
So, sometimes you want to express things like %5.2f
but mostly you don’t. The default case should let you be lazy (and simple) while still allowing complicated things. Perhaps:
"Hello %{user.name}, your account is %{account_number user}."
and to allow full formatting to be expressed:
"Hello! This float is %&5.2f{some_float}"
Though the specifics of that syntax need to be worked out, that looks slightly off.
Except all this doesn’t work with type inference, unless you use modular implicits. Consider the function let f x = Format.printf "Foo %{x}"
. What’s the type of x
? No, you can’t “fall down” to string
without breaking lot’s of other properties in the typesystem.
You can do it with modular implicits by assuming a Printable
signature, then you get f : {P:Printable} => P.t -> unit
.
Also, good luck writing a reliable type-directed transformation like that without basically putting it in the typechecker.
As for the rest, people here might be interested by https://github.com/janestreet/ppx_custom_printf
Yah, true. I hadn’t been thinking clearly. Yet another thing that you need Modular Implicits for.