January 13
On 1/13/24 03:16, Walter Bright wrote:
> 
> The other reasons:
> 
> 1. preventing calls to functions passing an ordinary string as opposed to an istring tuple
> 2. preventing nested istrings
> 
> have already been addressed. The compile time thing was the only one left.

There is more, e.g.:

- You cannot interpolate an expression sequence, it will cause the format string to get out of synch with the arguments.

- DIP1027's format strings allow manual overrides that may then be incompatible with the library functions.

- DIP1027's format strings require the user to manually escape e.g. '%s' so that the library does not mistake it for a format specifier, even though the user does not otherwise interact with format specifiers at all.
January 13

On Friday, 12 January 2024 at 22:35:54 UTC, Walter Bright wrote:

>

Given the interest in CTFE of istrings, I have been thinking about making a general use case out of it instead of one specific to istrings.

What happens here?

void pluto(string s = "default", Args...)(Args args)
{
}

void main () {
    pluto("foo");
    pluto("foo", "bar");
}
January 12
On 1/12/2024 6:56 PM, Timon Gehr wrote:
> On 1/13/24 03:16, Walter Bright wrote:
>>
>> The other reasons:
>>
>> 1. preventing calls to functions passing an ordinary string as opposed to an istring tuple
>> 2. preventing nested istrings
>>
>> have already been addressed. The compile time thing was the only one left.
> 
> There is more, e.g.:
> 
> - You cannot interpolate an expression sequence, it will cause the format string to get out of synch with the arguments.

I don't know what "interpolate an expression sequence" means. As for things getting out of sync, execi() with CTFE can reject a mismatch between format specifiers and arguments.

If you mean nested istrings, that can be simply rejected by not allowing a tuple argument for an argument. Or, execi() can detect arguments with the `Format` type and handle the nested istrings.


> - DIP1027's format strings allow manual overrides that may then be incompatible with the library functions.

execi() with CTFE can reject them.


> - DIP1027's format strings require the user to manually escape e.g. '%s' so that the library does not mistake it for a format specifier, even though the user does not otherwise interact with format specifiers at all.

execi() with CTFE can automatically escape them.
January 12
On 1/12/2024 7:05 PM, Andrej Mitrovic wrote:
> What happens here?
> 
> ```D
> void pluto(string s = "default", Args...)(Args args)
> {
> }
> 
> void main () {
>      pluto("foo");
>      pluto("foo", "bar");
> }
> ```

They will compile as:

```
pluto!"default"("foo");
pluto!"default"("foo", "bar");
```
i.e. as they would now. Existing conventional overloads would be the "better" match. Sliding templates would only be tried if the alternative was a failure to match.
January 13
On 1/13/24 04:36, Walter Bright wrote:
> 
> I don't know what "interpolate an expression sequence" means. As for things getting out of sync, execi() with CTFE can reject a mismatch between format specifiers and arguments.

Oh, not at all.

```d
import std.stdio;
alias Seq(T...)=T;
void main(){
    writefln(i"$(Seq!(1,2)) %s");
}
```
January 13

On Saturday, 13 January 2024 at 03:59:03 UTC, Timon Gehr wrote:

>

On 1/13/24 04:36, Walter Bright wrote:

>

I don't know what "interpolate an expression sequence" means. As for things getting out of sync, execi() with CTFE can reject a mismatch between format specifiers and arguments.

Oh, not at all.

import std.stdio;
alias Seq(T...)=T;
void main(){
    writefln(i"$(Seq!(1,2)) %s");
}

Yes, and there is more:

writefln(i"is it here? ${}(1) Or here? %s");

Bottom line is that if we make %s special, then all functions must deal with the consequences. There is not a format specifier you can come up with that is not easily reproduced in the string literal directly -- you have to escape it and know that you must escape it. The easier path is just not to deal with format specifiers at all -- tell the library exactly where the pieces are.

And by the way, your example brings up another point not recently brought up where 1036e handles and DIP1027 does not: tuples as interpolated expressions. Because each expression is enclosed by InterpolatedLiteral pieces, you can tell which ones were actually tuples.

-Steve

January 13

On Saturday, 13 January 2024 at 03:05:57 UTC, Andrej Mitrovic wrote:

>

What happens here?

void pluto(string s = "default", Args...)(Args args)
{
}

void main () {
    pluto("foo");
    pluto("foo", "bar");
}

From original post:

>
  1. the value parameters do not have default values

-Steve

January 13

On Saturday, 13 January 2024 at 02:16:06 UTC, Walter Bright wrote:

>

On 1/12/2024 4:15 PM, Steven Schveighoffer wrote:

>

I don't view this as simpler than DIP1036e or DIP1027 -- a simple transformation is a simple transformation.

Adding extra hidden templates isn't that simple. If a user is not using a canned version, he'd have to be pretty familiar with D to be able to write his own handler.

Yes, that is intentional. You should not be able to call functions with new syntax because the parameters happen to match. We have a type system for a reason.

>

1027 is simpler in that if the generated tuple is examined, it matches just what one would have written using a format. Nothing much to learn there.

In other words: "it matches just what one wouldn't have written, unless one is calling writef".

> >

Certainly a hybrid DIP1027 with a format string passed at compile time is still not viable, due to reasons already stated.

The other reasons:

  1. preventing calls to functions passing an ordinary string as opposed to an istring tuple

I don't see how this proposal fixes that. I'm assuming a function like void foo(string s, int x) will match foo(i"something: $(1)")

>
  1. preventing nested istrings

Why do we want to prevent nested istrings? That's not a goal.

>

have already been addressed. The compile time thing was the only one left.

A compile time format string still needs parsing. Why would we want to throw away all the work the compiler already did?

If you want to call writef, you can construct a format string easily at compile time. Or just... call writef the normal way.

Compile-time string parsing is way more costly than compile-time string concatenation.

> >

This does seem like it has the potential to break code:

The shifted one would be a more costly match, and so the legacy others would be favored first.

Ok. This does mean, for intentional overloading of a function to accept a compile-time first parameter, you will have to rename the function.

Possibly, if you have an opt-in syntax like Timon mentioned, then you can make the sliding template parameter more preferable.

-Steve

January 12
On 1/12/2024 8:13 PM, Steven Schveighoffer wrote:
> On Saturday, 13 January 2024 at 03:59:03 UTC, Timon Gehr wrote:
>> On 1/13/24 04:36, Walter Bright wrote:
>>>
>>> I don't know what "interpolate an expression sequence" means. As for things getting out of sync, execi() with CTFE can reject a mismatch between format specifiers and arguments.
>>
>> Oh, not at all.
>>
>> ```d
>> import std.stdio;
>> alias Seq(T...)=T;
>> void main(){
>>     writefln(i"$(Seq!(1,2)) %s");
>> }
>> ```

1027 can write a format string for a tuple as: "%s%s %%s" because the number of elements in the tuple is known at compile time.


> Yes, and there is more:
> 
> ```d
> writefln(i"is it here? ${}(1) Or here? %s");
> ```

An empty format ${} would be a compile time error. The %s would be rewritten as %%s.


> Bottom line is that if we make `%s` special, then all functions must deal with the consequences. There is not a format specifier you can come up with that is not easily reproduced in the string literal directly -- you have to escape it and *know* that you must escape it. The easier path is just not to deal with format specifiers at all -- tell the library exactly where the pieces are.

Escaping % is not hard to do. It's ordinary.


> And by the way, your example brings up another point not recently brought up where 1036e handles and DIP1027 does not: tuples as interpolated expressions. Because each expression is enclosed by `InterpolatedLiteral` pieces, you can tell which ones were actually tuples.

And with 1027 the format string will be of type `FormatString` (not `string`), and you can tell which ones were actually tuples. So I take that back, nested formats are easily supported.

These are all straightforward solutions.

January 12
On 1/12/2024 8:35 PM, Steven Schveighoffer wrote:
> On Saturday, 13 January 2024 at 02:16:06 UTC, Walter Bright wrote:
>> On 1/12/2024 4:15 PM, Steven Schveighoffer wrote:
>>> I don't view this as simpler than DIP1036e or DIP1027 -- a simple transformation is a simple transformation.
>>
>> Adding extra hidden templates isn't that simple. If a user is not using a canned version, he'd have to be pretty familiar with D to be able to write his own handler.
> 
> Yes, that is intentional.

So you agree it is not simpler :-)

> You should not be able to call functions with new syntax because the parameters happen to match. We have a type system for a reason.

I proposed in the other topic to type the format string as Format (or FormatString), which resolves that issue, as a string is not implicitly convertible to a FormatString.


>> 1027 is simpler in that if the generated tuple is examined, it matches just what one would have written using a format. Nothing much to learn there.
> 
> In other words: "it matches just what one wouldn't have written, unless one is calling `writef`".

Yes, it is meant for writef, not writeln.


>> The other reasons:
>>
>> 1. preventing calls to functions passing an ordinary string as opposed to an istring tuple
> 
> I don't see how this proposal fixes that. I'm assuming a function like `void foo(string s, int x)` will match `foo(i"something: $(1)")`

Yes, we've seen that example. It's a bit contrived. I've sent a format string to a function unexpectedly now and then. The result is the format string gets printed. I see it, I fix it. I can't see how it would be some disastrous problem. If it indeed a super problem, `Format` can be made to be a type that is not implicitly convertible to a string, but can have a string extracted from it with CTFE.

What it does fix is your other concern about sending a string to a function (like execi()) that expects a Format as its first argument.


>> 2. preventing nested istrings
> 
> Why do we want to prevent nested istrings? That's not a goal.

I mentioned in another reply to you a simple solution.


>> have already been addressed. The compile time thing was the only one left.
> 
> A compile time format string still needs parsing. Why would we want to throw away all the work the compiler already did?

For the same reason writefln() exists in std.stdio, and people use it instead of writeln(). Also, the SQL example generates a format string.


> If you want to call `writef`, you can construct a format string easily at compile time. Or just... call `writef` the normal way.

??

> Compile-time string parsing is way more costly than compile-time string concatenation.

I suspect you routinely use CTFE for far, far more complex tasks. This is a rounding error.


> Ok. This does mean, for *intentional* overloading of a function to accept a compile-time first parameter, you will have to rename the function.

You can overload it with existing functions, or give it a new name. Your choice, I don't see problem.