Discussion Thread: DIP 1036--String Interpolation Tuple Literals--Community Review Round 2
Q. Schroll
qs.il.paperinik at gmail.com
Thu Feb 4 18:14:59 UTC 2021
On Wednesday, 3 February 2021 at 23:00:54 UTC, Steven
Schveighoffer wrote:
> On 2/3/21 4:00 PM, Q. Schroll wrote:
>> An easy way out would be giving the actual type of i"..." a
>> property .interp or .__interp that (cf. tuple's expand)
>> returns the interp sequence. That way, it can be tested in
>> pragma(msg, i"...".__interp) while also being correct whenever
>> used in a canonical form. __interp couldn't really be a
>> library function. An alternative would be __traits(interp,
>> i"...").
>
> Yeah, with your scheme, it would have to be a compiler
> directive somehow.
I don't think this is a problem.
>> The interpolation tuple really is an implementation detail
>> that is relevant for an important, but still a rather small
>> minority of functions.
>
> I'm not sure I see it that way. The interpolation tuple allows
> one to accept string/data lists without allocation or
> transformation. That is super-powerful and super useful.
My approach is not to take that power away. My concern is that it
will hit you accidentally.
> I would agree that the major use case that most people will use
> is just to allocate a string.
I've thought about the formal semantics of my suggestion.
Formally, the compiler has to try the __interp and the string
version. If both succeed (that's the relevant case) and
instantiate THE SAME template, the string version (i.e. idup)
will be used. Otherwise, the __interp is a better match and will
be used.
This is precisely the case when there is a template that matches
interp sequences, but not strings.
For supporting both, regular and interpolated strings, you need
overloads, so that the interp sequence's match and the idup
string's match is different.
There was a question how to do `execute` for an SQL builder.
First, one overload would handle interp sequences. With that
exact semantics explained above, you (Steven in particular) can
use a contract, too:
auto execute(InterpSeq...)(InterpSeq interpSeq)
if (is(InterpSeq[0] == interp!str, string str))
{ /* handle interpolated string */ }
auto execute(Args...)(string sqlTemplate, Args args)
{ /* handle regular sql template */ }
That way, execute("SELECT $1 FROM table", name) only matches the
second and behaves as intended;
on the other hand, execute(i"SELECT ${name} FROM table") matches
both:
The first via execute(interp!"SELECT "(), name, interp!" FROM
table"())
and the second via execute(text("SELECT ", name, " FROM table")).
Because those are different templates, the first one is chosen
that uses the interp sequence.
If I'm not mistaken, you can even mix them: execute(i"SELECT $1
FROM ${table}", column) can be made work by the first overload.
However, when you do tuple(i"I have ${nBananas} bananas",
nBananas), both rewrites match the same template, so the rewrite
tuple(text("I have ", nBananas, " bananas"), nBananas) is chosen.
I don't think it can be made better without hitting usability.
With the proposal as-is, one could use `static if` and other
reflection in a single variadic template. Requiring a specific
overload to handle interpolated strings is probably the most
maintainable form anyway, so I don't consider that a big ask.
With that semantics, free form variadic templates aren't
instantiated in an unintended way. We cannot break all free form
variadic templates and hope them to anticipate and handle
interpolated strings. However, we can make writeln and friends
use interpolated strings' powerful lowering and even if we happen
to overlook a function that really should handle interpolated
strings, it can be added later.
Everyone wins.
More information about the Digitalmars-d
mailing list