January 29, 2021
On Thursday, 28 January 2021 at 06:12:14 UTC, Walter Bright wrote:
> It appears to have abandoned being usable with printf?

I suspect the sorts of people who use printf would probably continue using explicit format strings.

There is an axis of features from, for lack of better terms, "austere" to "luxurious". On that axis, printf is on one side and writefln/format are on the other. Format strings are a "luxurious" feature - like templates and DSLs, they give up fine-grained control over the actual function calls that take place, and in return gain expressivity and convenience. It's not like format strings break printf; printf remains callable as always. But if you are the sort of person who used to use a templated print function like formattedWrite, with its deep forest of generated code where you don't really care how it goes from input to output so much as that it just adequately prints the data you gave it with as little fuss as achievable - in other words, the sort of person who would welcome and prefer inline format strings - then you usually don't use printf to start with, because printf is a function that is *so* unintuitive and lowlevel that it needs its own *class* of linters.

You use printf when you want to precisely guide how your function arguments are parsed, down to the level of stack size. I don't think that is the usecase of format strings, and satisfying it will probably hurt the proposal.
January 29, 2021
Replying here to Walter's post in the Feedback Thread:

https://forum.dlang.org/post/rv0grf$g1j$1@digitalmars.com

> Let's compare mysql:
> 
> DIP1036:
> 
> mysql_query(i"select * from foo where id = ${obj.id} and val > ${minval}");
> 
> DIP1027:
> 
> mysql_query(i"select * from foo where id = ${?}(obj.id) and val > ${?}minval");
> 
> The DIP1027 could be shorter or longer, depending on if the ( ) were needed or not. DIP1036 also requires a user-written overload for mysql_query(), DIP1027 does not. DIP1036 is not a clear winner for mysql. 

Perhaps DIP1027 makes life easier for the library developer. As a _user_, however, DIP1036 wins by a landslide, because the placeholder is an internal implementation detail that needn't and shouldn't be exposed.

In fact, different SQL "dialects" can use different placeholders, so DIP1036 allows the following abstraction:

```
auto preparedStatement = connection.prepare(i"select * from foo where id = ${obj.id} and val > ${minval}")
```
This will then be translated by the actual class implementing the Connection interface to the proper version:

Postgres:

```
postgres_prepare("select * from foo where id = $1 and val > $2", obj.id, minval);
```

MySQL:
```
mysql_prepare("select * from foo where id = ? and val > ?", obj.id, minval);
```

Furthermore, the Postgres syntax becomes almost useless with DIP1027 if the user still has to keep track of the position of each argument, and for instance update all of them when adding one at the beginning.

As the user of a library, if I use an interpolated string, that's exactly the kind of thing I expect it to manage automatically for me.

Of course one could settle on a default placeholder and then reparse the string to adapt it as needed, that's in fact what JDBC does, but... here we have the chance to do the right thing from the beginning.

Finally, although a bit secondary, I personally find that the syntax in DIP1027 is somewhat awkward: If I see `${?}minval`, or even `${?}(obj.id)` I intuitively expect only `?` to be part of the interpolation, that's how it works in many languages (bash, PERL) when a variable is expressed as `${...}`, it specifically makes clear that everything out of the brackets is not included.
January 29, 2021
On 1/28/2021 12:46 AM, SealabJaster wrote:
> Finally, and I'm sorry if I missed this being described in the DIP, but consider this case (and similar):
> 
> ```
> void f(T)(T array)
> if(isArray!T)
> {
>      //...
> }
> 
> f(i"...");
> ```
> 
> What would happen here? My best guess is that it won't compile unless written as `f(i"...".idup)` which then introduces an odd discrepancy where there's certain usages that still require explicitly calling .idup, e.g. `i"...".idup.splitter`


This does appear to be an omission, please file it in the feedback thread.
January 29, 2021
On 1/28/2021 7:19 AM, bachmeier wrote:
> For those of us that want to do productive things with our time rather than fiddle around in the weeds of memory management, this is not a good sign. If I wanted to use Rust, I'd already be using it.

Sure, but #DIP1027 did not require a GC or fiddling around. So it's not like it's not possible.
January 29, 2021
On 1/29/2021 2:53 AM, Arafel wrote:
> Of course one could settle on a default placeholder and then reparse the string to adapt it as needed, that's in fact what JDBC does, but... here we have the chance to do the right thing from the beginning.

I bet it could be done with a template overload of the function call. This doesn't make it worse than DIP1036, which requires template overloads written by the user anyway.
January 29, 2021
On Friday, 29 January 2021 at 08:57:13 UTC, Walter Bright wrote:
> Even if it becomes part of Phobos, overloading C functions imported from external C libraries in general makes me uncomfortable, because it hides the fact that the C function isn't being called. I never liked C macros like:
>
>     #define printf(...) printf("abc" ...)
>
> for the same reason.

In that case we can just pick a different for the mapper function. `printI` for example.
January 29, 2021
On Friday, 29 January 2021 at 11:09:57 UTC, Walter Bright wrote:
> Sure, but #DIP1027 did not require a GC or fiddling around. So it's not like it's not possible.

You need to remember that perhaps the most important use case for string interpolation are string mixins. With DIP1027, one would have to write `mixin(i"foreach(i;0..${iterations}) foo()".format)` instead of `mixin(i"foreach(i;0..${iterations}) foo()")` that works with this proposal. Added `.format` for every single interpolated string mixin.

Let's face it, having to import a library function to use `printf` with interpolated strings is a small price to pay to avoid the first issue.
January 29, 2021
On Friday, 29 January 2021 at 12:09:26 UTC, Dukc wrote:
> On Friday, 29 January 2021 at 11:09:57 UTC, Walter Bright wrote:
>> Sure, but #DIP1027 did not require a GC or fiddling around. So it's not like it's not possible.
>
> You need to remember that perhaps the most important use case for string interpolation are string mixins. With DIP1027, one would have to write `mixin(i"foreach(i;0..${iterations}) foo()".format)` instead of `mixin(i"foreach(i;0..${iterations}) foo()")` that works with this proposal. Added `.format` for every single interpolated string mixin.

You cannot actually pass an i"..." literal directly to `mixin` with this proposal; you have to call `idup` (or some other function) to convert it to a string first.
January 29, 2021
On Thursday, 28 January 2021 at 14:58:36 UTC, Steven Schveighoffer at the feedback theard wrote:
> On 1/28/21 3:35 AM, Dukc wrote:
>> The DIP states that foo(i"a:${a}, ${b}.") is rewritten as `foo(Interp!"a:", a, Interp!", ", b, Interp!".")`. It think it's better to rewrite it as `foo(Interp!"a:", Interp!typeof(a)(a), Interp!", ", Interp!typeof(b)(b), Interp!".")`. That way, `foo` has easier time introspecting which came from the interpolated string.
>
> First, I don't think it's critical for overloading, and will simply add to the template bloat. What are you going to do differently with `a` than you would with `Interp!(typeof(a))(a)`?

I was mainly thinking that I'd have easier time differentiating between an `int` in interpolated string and `int` passed before/after the interpolated string. And I have a type that will implicitly convert to string if I want to do that - no need to call `to!string(a)` or `a.Interp!(typeof(a))` first.

> The parameters are guaranteed to start and end with an InterpolationLiteral, so one can assume that non-literal arguments are interspersed inside the literal.

It can be done, but it sounds more complex for the introspecting function. I'm not strict about this though, what the DIP now proposes be worth it to be able to pass `ref` parameters in interpolated strings.

>
>> The type of interpolated string literal is very special cased. [snip]
>
> I was fully aware that this would be the most controversial part. I feel like it will not be full of corner cases, but I'm not sure. Can you specify any?
>
> Consider a normal string literal can be used as a string, immutable(char)*, wstring, or dstring. I find it very similar to this feature, and I don't feel like there are a lot of corner cases there.

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.

The compromise that the interpolated string would be an expanded tuple, that would be implicitly assignable to string via value range propagation mechanics, sounds acceptable. But it needs to be clear IMO what the primary type of an interpolated string is. If it is not an expanded tuple, what it is then? I mean that this must be guaranteed to pass IMO:

```
auto interpolation = i"&{apples} apples and ${oranges} oranges";

static pure int foo(typeof(interpolation));
static void foo(string){assert(0,"this must not be called");}

assert(foo(interpolation) == foo(i"&{apples} apples and ${oranges} oranges"));
```

>
>> Let me suggest an alternative: [snip]
>> 
>
> We have considered that. The problem is that people will use the string interpolation form without realizing the dangers or resulting bloat.
>
> For instance, writeln(i"Hello, ${name}"), if made to proactively generate a string just to send it to writeln is extremely wasteful when writeln(I"Hello, ${name}") is not.

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.

Or did you mean template bloat? From that perspective I don't see difference. The first case will cause a new `idup` instance that interprets `name`, the second will cause a `writeln` instance that has similar interpretation mechanics.

>
> Consider also that code which uses a dual-literal system might have to use the string interpolation form because the library only allows that. Then at some point in the future, the library adds support for the expanded form. Now the user would have to go back and switch all usage to that new form, whereas an auto-rewrite would just work without changes.

True, but the user can just continue to use the old form. If it was good enough until then, why hurry switching? Besides, the migration path is very easy.



January 29, 2021
On 29/1/21 12:14, Walter Bright wrote:
> On 1/29/2021 2:53 AM, Arafel wrote:
>> Of course one could settle on a default placeholder and then reparse the string to adapt it as needed, that's in fact what JDBC does, but... here we have the chance to do the right thing from the beginning.
> 
> I bet it could be done with a template overload of the function call. This doesn't make it worse than DIP1036, which requires template overloads written by the user anyway.

You seem to refer to the library author as the "user", for me the "user" is the actual user of the library, and I'd say this kind of user is much more important (if only because there are more of them). It is them who should have an easy time, even if it requires a bit more effort from the library author.

And I'm not sure how a template overload could possibly work with interfaces. The database driver often needs to be chosen at runtime, so you get something like this:

```
string dbDriver = readConfig("dbDriver");
auto connection = Driver.getDriver(dbDriver).getConnection(/* connection parameters * /);
auto result = connection.execute(i"select * from foo where id = ${obj.id} and val > ${minval}"); // Needn't care about the internals
```

That's how it should look like for the end user.

I don't see how the syntax of your example of DIP1027 could be easier than this for the real end-user... and not even for the author of the library.

Just for completion, this is how the SQL library might look like (shameless copy of the structure of JDBC):

```
class Driver {
	static public Driver getDriver(string name) { /* ... */ }
	static public void register(Driver driver, string name) { /* ... */ } // To be called by each driver in `shared static this`
	abstract public Connection getConnection( /* ... */ );
}
interface Connection {
    // ...
    public Result execute(/* ... */);
}
interface Result { /* ... */ }

class MySQLConnection : Connection {
    // ...
    override public Result execute(/* ... */) {
        /* uses msql_query(...) and `?` */
    }
}

class PostgreSQLConnection : Connection {
    // ...
    override public Result execute(/* ... */) {
        /* uses postgres_query(...) and `$1`, `$2`, etc. */
    }
}
```