Discussion Thread: DIP 1036--String Interpolation Tuple Literals--Community Review Round 2

Steven Schveighoffer schveiguy at gmail.com
Sat Jan 30 00:50:04 UTC 2021


On 1/29/21 5:39 PM, Dukc wrote:
> On Friday, 29 January 2021 at 19:10:55 UTC, Steven Schveighoffer wrote:
>> On 1/29/21 7:58 AM, Dukc wrote:
>>> A string literal is a string that is implicitly assignable to the 
>>> other alternatives via value range propagation mechanics, or that's 
>>> how I understand it at least.
>>
>> No, this isn't range-value propagation. There is no way to recreate or 
>> save the type that is a string literal.
> 
> It may be that VRP is not the correct term, but I meant that a string 
> literal (just like an array literal, or VRPed integers) has one 
> unambiguous primary type, that is used if it's not immediately assigned 
> to something else.

This is mostly the same as that. The difference is in when you try 
things outside of the world of function calls, it uses the conversion 
unconditionally. This includes mixin, typeof, auto variables.

It's slightly different from normal rules, and I'm very much interested 
in hearing ways this will break, but I also am still optimistic it is valid.

> 
>> D has, however, added things like typeof(null), which still work as 
>> polysemous values (assignable to multiple types).
> 
> But even there: it has a primary type that is tried first, before any 
> conversion rules kick in. Unlike what you're proposing.

Right, the preferred type isn't really a type. Which is what makes it 
strange in most cases where you would expect to see the results from the 
preferred type (i.e. typeof, auto variables). But I still think it 
works, because of the narrow scope of the expanded form usage. For 
almost all intents and purposes, the thing is a string, unless you 
accept it as a sequence.

If this DIP goes down, a possibility is to use a different mechanism to 
ask for the expanded form other than providing a parameter list that 
matches. I just like the parameter list matching because it's simple and 
already understood.

> 
>>
>>> I mean that this must be guaranteed to pass IMO:
>>>
>>> [snip]
>>
>> No, that will not pass, and is guaranteed not to pass. 
>> typeof(interpolation) is string.
>>
>> Just like this wouldn't pass:
>>
>> auto x = 1, 2;
>>
>> int foo(int, int);
>>
>> foo(x);
>>
>> The compiler wouldn't allow it, and would rewrite with idup, yielding 
>> a string.
> 
> You need to specify the rules about the behaviour of the expression 
> unambiguously, and that is going to be a lot harder if you don't allow 
> yourself the luxury of using a primary type.\
> 
> If I understood what you're proposing, the expanded form is attempted 
> only when the interpolated string is an argument to a function or a 
> template. But this still leaves a lot of questions:
> 
> 1: Is the expanded form attempted inside constructors?

The expanded form is attempted when calling a constructor, it's just a 
function call.

So new Foo(i"...") attempts the expanded form first, and if it does not 
match, uses the idup rewrite.

> 2: Is the expanded form attempted if the interpolated string is passed 
> as first argument in UFCS style?

UFCS works by putting arguments on the left side of the dot first, so we 
are counting on it working with the expanded form. Otherwise, something 
like i"hello, ${name}".idup will not work correctly.

> 3: What a variable with `enum` storage class stores an interpolated 
> string and that gets passed to a function?

It would be a string type, just like the auto storage class.

> 4: What does this template do when called with interpolated string? 
> `auto foo(T...)(T arg){bar(arg);}`

foo would receive the expanded form, bar would only work if it accepted 
the expanded form (no rewrite is done there). Once the compiler decides 
not to rewrite into a string, it's a tuple for the duration. Again, this 
is not a type that implicitly converts, it's a rewrite by the compiler.

This is similar to:

void bar(const char *);

foo(T)(T arg) { bar(arg); }

bar("hello"); // ok
foo("hello"); // error

> 5: If an interpolated string gets called by an operator, what happens?

do you mean something like opAssign(i"...")? It should work the same as 
other function calls.

> 6: Probably much more issues like these.

The rules are pretty straightforward, so I'm happy to tell you the answers.

> 
>>> I don't think it's that bad, we tend to do stuff like 
>>> `writeln("hello, " ~ name)` anyway. Relatively small inefficiencies 
>>> like this don't matter in non-critical places, and critical places 
>>> need to be benchmarked and optimized anyway.
>>
>> I don't consider it a small inefficiency to involve all of the 
>> machinery of generating a string just to throw it away after printing.
>>
>> But in any case, it's unnecessary without good reason.
> 
> The reason would be to have an unambiguous type for the string literal. 
> I'd much rather have inefficiencies like the one mentioned, that I can 
> manually optimise away if needed, than complex language rules that are 
> likely to result in implementation bugs.

If there are implementation bugs, we can deal with them. If there are 
design problems, I want to figure them out now. So please, continue to 
try and figure out what could be wrong with this scheme!

To put it another way, in the case where the "dual-mode" interpolation 
literals work and my rewrite scheme work, I'd prefer the rewrite scheme. 
If there is a killer problem which makes the rewrite scheme non-viable, 
then we have to consider other options, including dropping the rewrite.

-Steve


More information about the Digitalmars-d mailing list