The state of string interpolation
H. S. Teoh
hsteoh at quickfur.ath.cx
Fri Dec 7 19:52:37 UTC 2018
On Fri, Dec 07, 2018 at 01:46:05PM -0500, Steven Schveighoffer via Digitalmars-d wrote:
[...]
> text(iq"CODETEMPLATE
> void ${funcName}(${funcParams.formatted!"%-(%s, %)"}) {
> ${generateBoilerplate}
> for (i; 0 .. ${numIter})
> {
> ${generateFuncBody}
> }
> }
> CODETEMPLATE");
>
> Oh, and it requires zero library support. It just works, as if you
> called it with the parameters in the right place.
The irony is that formatted!"%-%(s, %)" is essentially std.format
wrapped in nice clothes, the very thing which we're trying to replace
with string interpolation, and the very example of my point, which is
that (standard) string interpolation syntax is limited in what it can
express before you have to resort to inline code. If we didn't have
std.format, you'd have to come up with various helper functions for
formatting stuff, which is essentially an escape hatch from the
simplistic syntax of string interpolation. Without the std.format DSL,
the only thing we have left is "string with embedded code snippets".
Of course, we could extend the string interpolation syntax with
shorthands for common formatting operations, like array formatting,
width/precision specifiers, etc.. But then you end up reinventing the
std.format DSL (perhaps with a different syntax, which may or may not be
nicer).
> With:
>
> alias argf = formatted!("%-(%s, %)";
>
> then the function declaration becomes:
> void ${funcName}(${funcParams.argf}) {
[...]
Again, the irony is that we're depending on std.format for the
formatted!... template. So why go through the trouble of implementing
string interpolation instead of just using std.format to begin with?
Now, mind you, I've nothing against string interpolation, in the
language or as a library. All I'm saying is that having a DSL does make
certain things easier to express, though the tradeoff is that you have
to learn (possibly obscure-looking) new syntax. Dispensing with a
complex DSL leaves you with convenient basic formatting, but when you
need something more than basic formatting, you have to drop down to
code.
//
What would be potentially awesome, is if we take the best of both worlds
and combine them into a unified DSL that is both powerful (e.g. with
concise formatting capabilities like std.format) and readable (by
allowing in-place variable names). Then you could write something like
this:
int idx = 20;
string varname = "myMatrix";
float[][] data = [
[ PI, 1, 0 ],
[ -1, -PI, 0 ],
[ 0, 0, -1 ]
];
auto s = formatNG!("Matrix #0x%{idx}02X '%{varname}s' contains:\n"~
"%{data}(\t[ %(%5.2f %) ]%|\n%)");
and it would output:
Matrix #0x14 'myMatrix' contains:
[ 3.14 1.00 0.00 ]
[ -1.00 -3.14 0.00 ]
[ 0.00 0.00 -1.00 ]
This would be another application for passing function context as a
template parameter. Once we have that in the language, the
implementation of formatNG could potentially look something like this:
template formatNG(string fmt, context = __traits(getContext))
{
...
enum varName = ...;
result ~= formatItem(mixin("context." ~ varName));
...
}
where the default template argument fetches the caller's context by
default.
T
--
Do not reason with the unreasonable; you lose by definition.
More information about the Digitalmars-d
mailing list