The state of string interpolation

Steven Schveighoffer schveiguy at gmail.com
Fri Dec 7 18:46:05 UTC 2018


On 12/7/18 1:26 PM, H. S. Teoh wrote:
> On Fri, Dec 07, 2018 at 05:11:35PM +0000, Atila Neves via Digitalmars-d wrote:
> [...]
>> Every language change is a cost, and therefore should justify its
>> inclusion.  I personally don't think that it is in this case just to
>> make a niche use case slightly easier, and this coming from someone
>> from that niche! From now on it's:
>>
>> const code = mixin(interp!q{
>>      // stuff that I used to write with many %s and it was hard to match them
>> up
>> });
> 
> One thing I like about format() that string interpolation doesn't
> (easily) give, is the ability to specify additional formatting
> parameters, e.g., %6.2f, or %04X. You'd have to basically "uglify"
> string interpolation in much the same ways in order to get equivalent
> functionality, e.g., (hypothetical syntax) "Price is ${6.2:price}.".

You'd need a valid D expression there with the given proposal (to lower 
to a tuple). Something like ${price.formatted!("%6.2s")}.

Or as I had suggested elsewhere, you could use UDAs:

@formatted!("%6.2s") double price;

It's not exactly easy, but it's doable. Plus you could alias it:

alias moneyf = formatted!("%6.2s");
${price.moneyf}

Which makes things actually quite pleasant.

> 
> The one thing missing from format(), as you point out above, is the lack
> of named arguments for long format strings.  This could be fixed by
> extending format() with named placeholders, so that you can do something
> like:
> 
> 	// Hypothetical syntax
> 	format(q"CODETEMPLATE
> 		void %{funcName}s(%{funcParams}(%s, %)) {
> 			%{boilerPlate}s
> 			for (i; 0 .. %{numIter}d)
> 			{
> 				%{loopBody}s
> 			}
> 		}
> 	CODETEMPLATE", NamedArgs([
> 		"funcName", "myFunc",
> 		"funcParams", ["int x", "float y", "string z"],
> 		"boilerPlate", generateBoilerplate(),
> 		"loopBody", generateFuncBody(),
> 		"numIter", 255
> 	]));
> 
> Basically, instead of sequential arguments (or numerically-indexed
> arguments like $#1), format takes what amounts to a polymorphic
> associative array from which it can look up named arguments. This gets
> rid of the "too many %s I can't figure out which argument is which"
> problem, plus it makes your format string more readable and
> maintainable. It allows the same argument to be referenced multiple
> times, and arguments don't have to appear in the order they appear in
> the format string (important for i18n uses).

The benefit I see from named parameters is using them more than once. 
That snippet above is very unreadable IMO.

But just for fun, let's compare to string interpolation strawman:

	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.

With:

alias argf = formatted!("%-(%s, %)";

then the function declaration becomes:
		void ${funcName}(${funcParams.argf}) {

-Steve


More information about the Digitalmars-d mailing list