December 06, 2018
On 12/6/18 4:01 AM, Dennis wrote:
> On Thursday, 6 December 2018 at 02:14:12 UTC, Neia Neutuladh wrote:
>> However, that would also force druntime to include formatting code that it currently lacks.
> 
> Jonathan Marler's implementation [1] has a really nice approach of lowering interpolated strings to tuples. You can pass it to variadic functions like `writeln` or `text` and bring your own formatting / memory allocation schemes, giving better performance and flexibility.
> 
> [1] https://github.com/dlang/dmd/pull/7988

I supported the concept[1] back when I read it from Dmitry Olshansky[2].

If I had known about the debate on the PR I would have jumped in. But I don't have much to add. All that really dictates my views is the experience I've had with other languages. However, none of those other languages have the same concept that I know of -- they basically just create a string with toString-ing the interpolations.

With the concept of lowering to a tuple, I'd love to use such a thing for database queries.

For instance:

db.exec("UPDATE Foo SET a = ?, b = ?, c = ?, d = ? WHERE id = ?", aval, bval, cval, dval, id);

vs.

db.exec(i"UPDATE Foo SET a = $aval, b = $bval, c = $cval, d = $dval WHERE id = $id");

The mixin/library solution is much less approachable. What's awesome about the language solution is that it just works without much extra understanding and verbosity, and need for using mixins. mixins are cool, but are best tucked away behind templates or generative functions. They shouldn't seen much in user code. Saying we should use a library solution for this is like saying we can replace foreach usage with a library foreach that lowers to some for-loop and explicit delegates (or maybe a mixin?). Yes, it could be done. No, it shouldn't be done. This is one of those types of syntax sugar that should not be ignored.

+1000 from me, I'd love to see the PR merged, or the DIP created, whatever needs to happen.

-Steve

[1] https://forum.dlang.org/post/odb9hk$2jqm$1@digitalmars.com
[2] https://forum.dlang.org/post/ocut06$261n$1@digitalmars.com
December 06, 2018
On Thursday, 6 December 2018 at 14:02:09 UTC, Adam D. Ruppe wrote:
> On Thursday, 6 December 2018 at 09:01:03 UTC, Dennis wrote:
>> Jonathan Marler's implementation [1] has a really nice approach of lowering interpolated strings to tuples.
>
> Yes, yes, yes, I like that a lot. I think that's the way to do it if D is ever to get these.
>
> I propose that we all either
>
> 1) drop the idea
>
> or
>
> 2) unite around that proposal and try to push it through.

I don't mind spearheading the charge, but I am not going to do this all by myself as I got the other DIP in mind(Most noticeably, making properties great again, as I have to pick up the torch that the other dev left off.)
Let arrange a meet up and let us do this thing.
December 06, 2018
On Thursday, 6 December 2018 at 14:02:09 UTC, Adam D. Ruppe wrote:
> On Thursday, 6 December 2018 at 09:01:03 UTC, Dennis wrote:
>> Jonathan Marler's implementation [1] has a really nice approach of lowering interpolated strings to tuples.
>
> Yes, yes, yes, I like that a lot. I think that's the way to do it if D is ever to get these.
>
> I propose that we all either
>
> 1) drop the idea
>
> or
>
> 2) unite around that proposal and try to push it through.

O.k. I take 2) how is the best way to "unite around that proposal?"
December 06, 2018
On Thursday, 6 December 2018 at 16:19:12 UTC, Steven Schveighoffer wrote:
>
> [snip]
>
> For instance:
>
> db.exec("UPDATE Foo SET a = ?, b = ?, c = ?, d = ? WHERE id = ?", aval, bval, cval, dval, id);
>
> vs.
>
> db.exec(i"UPDATE Foo SET a = $aval, b = $bval, c = $cval, d = $dval WHERE id = $id");
>

Very pretty.
December 06, 2018
On Thursday, 6 December 2018 at 16:19:12 UTC, Steven Schveighoffer wrote:
> On 12/6/18 4:01 AM, Dennis wrote:
>> [...]
>
> I supported the concept[1] back when I read it from Dmitry Olshansky[2].
>
> If I had known about the debate on the PR I would have jumped in. But I don't have much to add. All that really dictates my views is the experience I've had with other languages. However, none of those other languages have the same concept that I know of -- they basically just create a string with toString-ing the interpolations.
>
> With the concept of lowering to a tuple, I'd love to use such a thing for database queries.
>
> For instance:
>
> db.exec("UPDATE Foo SET a = ?, b = ?, c = ?, d = ? WHERE id = ?", aval, bval, cval, dval, id);
>
> vs.
>
> db.exec(i"UPDATE Foo SET a = $aval, b = $bval, c = $cval, d = $dval WHERE id = $id");
>
> The mixin/library solution is much less approachable. What's awesome about the language solution is that it just works without much extra understanding and verbosity, and need for using mixins. mixins are cool, but are best tucked away behind templates or generative functions. They shouldn't seen much in user code. Saying we should use a library solution for this is like saying we can replace foreach usage with a library foreach that lowers to some for-loop and explicit delegates (or maybe a mixin?). Yes, it could be done. No, it shouldn't be done. This is one of those types of syntax sugar that should not be ignored.
>
> +1000 from me, I'd love to see the PR merged, or the DIP created, whatever needs to happen.
>
> -Steve
>
> [1] https://forum.dlang.org/post/odb9hk$2jqm$1@digitalmars.com
> [2] https://forum.dlang.org/post/ocut06$261n$1@digitalmars.com

Does I understand your sql example right, although it looks like it is prone for sql injection attacks, it isn't because you evaluate the tuples and not use the string as whole?

I really like this feature. Everytime I have to concatenate strings and variables I really really miss this feature.

I would propose Mike starts a donation campaign for this DIP. Then op and others can donate in an official way.

Kind regards
Andre
December 06, 2018
On Thursday, 6 December 2018 at 17:47:58 UTC, Andre Pany wrote:
> Does I understand your sql example right, although it looks like it is prone for sql injection attacks, it isn't because you evaluate the tuples and not use the string as whole?

Yeah, since it is tuples the function itself gets to manage how they are used, including doing some escaping, etc.

I would take it one step further and put the other stuff in a wrapped type from the compiler, so the function receiving it can static if and tell what it is, so

i"foo $(foo)"
would be

tuple("foo ", FromInterpolation("foo", foo))


so you can identify when something was passed vs being literally in the string. And it included the name as a string so we can do some other crazy stuff too.

i"foo $(a + b)"

FromInterpolation("a + b", a+b)

so you can then print

a + b = 4


i think that would be kinda cool - the stuff inside is passed as a code string. So really generally speaking it would be


tuple("string literal", FromInterpolation(code_as_string, mixin(code)), " more string literal"); // and so on


I think that would be seriously cool and quite useful. You can then see From Interpolation as a type in there and know to call sql escape or replace with ? and move the arg or whatever - the function can use it all with CT reflection.
December 06, 2018
On 12/6/18 12:47 PM, Andre Pany wrote:
> On Thursday, 6 December 2018 at 16:19:12 UTC, Steven Schveighoffer wrote:
>> For instance:
>>
>> db.exec("UPDATE Foo SET a = ?, b = ?, c = ?, d = ? WHERE id = ?", aval, bval, cval, dval, id);
>>
>> vs.
>>
>> db.exec(i"UPDATE Foo SET a = $aval, b = $bval, c = $cval, d = $dval WHERE id = $id");
>>
> Does I understand your sql example right, although it looks like it is prone for sql injection attacks, it isn't because you evaluate the tuples and not use the string as whole?

Yes, that's exactly right, the whole thing gets lowered into:

db.exec("UPDATE Foo SET a = ", aval, ", b = ", bval, ", c = ", cval, ", d = ", dval", " WHERE id = ", id);

The one thing that this requires, in order to not allocate a string to pass to the SQL engine, is direct protocol access, such as we have in mysql-native.

There are other possibilities too. For instance, it's possible we could somehow pass the strings for compile time, so the real SQL string is generated internally. But I don't know how that would look. Perhaps, you just pass the query string at compile time, and deal with the tuple to generate the string.

-Steve
December 06, 2018
On Thu, 06 Dec 2018 18:06:51 +0000, Adam D. Ruppe wrote:
> I would take it one step further and put the other stuff in a wrapped type from the compiler, so the function receiving it can static if and tell what it is, so
> 
> i"foo $(foo)"
> would be
> 
> tuple("foo ", FromInterpolation("foo", foo))

I was about to suggest wrapping the non-parameters in a Literal{} struct, but FromInterpolation makes more sense.

I was thinking about protecting against errors produced when you have to use an even/odd rule to figure out what's part of the literal and what's part of the interpolation:

    auto c = ");drop table foo;--";
    // whoops, forgot a comma
    db.exec("SELECT * FROM foo WHERE id IN ($a,$b$c)");
      ->
    db.prepare("SELECT * FROM foo WHERE id IN(?, ?);drop table foo;--?")
      .inject(a, b, ")");

With FromInterpolation, you'd be able to reliably come up with the correct SQL: "SELECT * FROM foo WHERE id IN (?, ??)". Which is invalid and would be rejected.
December 06, 2018
On Thursday, 6 December 2018 at 18:06:51 UTC, Adam D. Ruppe wrote:
> I would take it one step further and put the other stuff in a wrapped type from the compiler, so the function receiving it can static if and tell what it is, so
>
> i"foo $(foo)"
> would be
>
> tuple("foo ", FromInterpolation("foo", foo))
>
>
> so you can identify when something was passed vs being literally in the string. And it included the name as a string so we can do some other crazy stuff too.
>
> i"foo $(a + b)"
>
> FromInterpolation("a + b", a+b)
>
> so you can then print
>
> a + b = 4
>
> [...]

Very neat idea.
December 06, 2018
On 12/6/18 1:06 PM, Adam D. Ruppe wrote:
> On Thursday, 6 December 2018 at 17:47:58 UTC, Andre Pany wrote:
>> Does I understand your sql example right, although it looks like it is prone for sql injection attacks, it isn't because you evaluate the tuples and not use the string as whole?
> 
> Yeah, since it is tuples the function itself gets to manage how they are used, including doing some escaping, etc.
> 
> I would take it one step further and put the other stuff in a wrapped type from the compiler, so the function receiving it can static if and tell what it is, so
> 
> i"foo $(foo)"
> would be
> 
> tuple("foo ", FromInterpolation("foo", foo))
> 
> 
> so you can identify when something was passed vs being literally in the string. And it included the name as a string so we can do some other crazy stuff too.
> 
> i"foo $(a + b)"
> 
> FromInterpolation("a + b", a+b)
> 
> so you can then print
> 
> a + b = 4
> 
> 
> i think that would be kinda cool - the stuff inside is passed as a code string. So really generally speaking it would be
> 
> 
> tuple("string literal", FromInterpolation(code_as_string, mixin(code)), " more string literal"); // and so on
> 
> 
> I think that would be seriously cool and quite useful. You can then see  From Interpolation as a type in there and know to call sql escape or replace with ? and move the arg or whatever - the function can use it all with CT reflection.

It would be useful, but I don't like the mechanism. It requires you have a lot more machinery to deal with the paramters, where as now it Just Works with things like writeln.

It could be an extra piece of data that is accessible from the compile-time tuple:

foo(i"a + b = $(a+b));

void foo(Params...)(Params p) {
   static assert(__traits(getInterpolation, p[1]) == "a+b");
   static assert(__traits(getInterpolation, p) == AliasSeq!("a + b = ", "a+b"));
}

Maybe __traits(getInterpolation, ...) returns empty sequence for non-interpolated portions? Or maybe it's an error?

Just thought of a cool other possibility, if the aliases are accessible in the function:

@formatSpec("%x") int a;

writeln(i"a is $a"); -> prints hex version of `a`

The downside of this is that there is potentially a separate instantiation for when string interpolation is used, vs. when the parameters are passed normally. Maybe that means it requires you use compile-time arguments. A small price to pay I would think.

-Steve