www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - What do you thing about this string interpolation idea

reply Daniel Kozak <kozzi11 gmail.com> writes:
https://run.dlang.io/is/FbOnGI

import std.stdio;
template somefun()
{
      auto iterpolate(string s)()
      {
          //do some parsing
          return mixin(s[1 .. $]);
      }
}

enum enableInterpolate = "mixin somefun A; alias interpolate = 
A.iterpolate;";

void main()
{
     mixin(enableInterpolate);
     int a = 5;
     iterpolate!("$a").writeln;
}
Dec 10 2018
next sibling parent reply Daniel Kozak <kozzi11 gmail.com> writes:
https://gist.github.com/run-dlang/a0dce06b873b216680df673661e4b4e6
Dec 10 2018
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Monday, 10 December 2018 at 12:34:33 UTC, Daniel Kozak wrote:
 https://gist.github.com/run-dlang/a0dce06b873b216680df673661e4b4e6
While I'm not sure of all the details of the implementation, I think the use of a mixed in templated nested function to enable access to the local scope without string mixin is inspired. Very clever idea.
Dec 10 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/10/18 7:45 AM, FeepingCreature wrote:
 On Monday, 10 December 2018 at 12:34:33 UTC, Daniel Kozak wrote:
 https://gist.github.com/run-dlang/a0dce06b873b216680df673661e4b4e6
While I'm not sure of all the details of the implementation, I think the use of a mixed in templated nested function to enable access to the local scope without string mixin is inspired. Very clever idea.
Yeah, this looks a lot better than mixing in every interpolation. I tried it out with adding arbitrary expressions, and it works like a charm: https://gist.github.com/run-dlang/6f682fe6ca12e02acc1c6af2c67632e7 It needs some polish around the parsing (need to handle parentheses and separating symbols with something other than space), but I'm pretty happy with the concept. -Steve
Dec 10 2018
parent reply aliak <something something.com> writes:
On Monday, 10 December 2018 at 15:50:58 UTC, Steven Schveighoffer 
wrote:
 On 12/10/18 7:45 AM, FeepingCreature wrote:
 On Monday, 10 December 2018 at 12:34:33 UTC, Daniel Kozak 
 wrote:
 https://gist.github.com/run-dlang/a0dce06b873b216680df673661e4b4e6
While I'm not sure of all the details of the implementation, I think the use of a mixed in templated nested function to enable access to the local scope without string mixin is inspired. Very clever idea.
Yeah, this looks a lot better than mixing in every interpolation. I tried it out with adding arbitrary expressions, and it works like a charm: https://gist.github.com/run-dlang/6f682fe6ca12e02acc1c6af2c67632e7 It needs some polish around the parsing (need to handle parentheses and separating symbols with something other than space), but I'm pretty happy with the concept. -Steve
This is much better than having to mixin everywhere. A couple of things: 1) Can this be put in a module so that you don't have to mixin(enableInterpolation) but instead "import interp = std.interpolation;" or something similar? 2) This would unfortunately pollute the namespace and in code that uses interpolation heavily, you'd have to rename it to something non-clashy which I can see being quite annoying and a maintenance burden if it turns out to be a common thing to do. Maybe if it's just standardly called "interp" it may be alright though. Cheers, - Ali
Dec 10 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/10/18 11:11 AM, aliak wrote:
 This is much better than having to mixin everywhere. A couple of things:
 
 1) Can this be put in a module so that you don't have to 
 mixin(enableInterpolation) but instead "import interp = 
 std.interpolation;" or something similar?
No, you need a local mixin. Doing that import just imports the *symbol* into your namespace, but it doesn't give access to your namespace to the symbol.
 2) This would unfortunately pollute the namespace and in code that uses 
 interpolation heavily, you'd have to rename it to something non-clashy 
 which I can see being quite annoying and a maintenance burden if it 
 turns out to be a common thing to do. Maybe if it's just standardly 
 called "interp" it may be alright though.
We can pass in the name we want to avoid conflicts. So something like: import std.interpolation; mixin(enableInterpolate!"interp"); interp!("$a").writeln; -Steve
Dec 10 2018
parent reply Aliak <something something.com> writes:
On Monday, 10 December 2018 at 16:27:03 UTC, Steven Schveighoffer 
wrote:
 On 12/10/18 11:11 AM, aliak wrote:
 This is much better than having to mixin everywhere. A couple 
 of things:
 
 1) Can this be put in a module so that you don't have to 
 mixin(enableInterpolation) but instead "import interp = 
 std.interpolation;" or something similar?
No, you need a local mixin. Doing that import just imports the *symbol* into your namespace, but it doesn't give access to your namespace to the symbol.
Au :(. Yeah that makes sense. Then I’m not sure I see how this improves things if it has to be mixed in to every scope you want to use interpolation for. The sparseness of interpolation might just make mixin(Interp!””)); more appealing.
Dec 10 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/10/18 11:36 AM, Aliak wrote:
 On Monday, 10 December 2018 at 16:27:03 UTC, Steven Schveighoffer wrote:
 On 12/10/18 11:11 AM, aliak wrote:
 This is much better than having to mixin everywhere. A couple of things:

 1) Can this be put in a module so that you don't have to 
 mixin(enableInterpolation) but instead "import interp = 
 std.interpolation;" or something similar?
No, you need a local mixin. Doing that import just imports the *symbol* into your namespace, but it doesn't give access to your namespace to the symbol.
Au :(. Yeah that makes sense. Then I’m not sure I see how this improves things if it has to be mixed in to every scope you want to use interpolation for. The sparseness of interpolation might just make mixin(Interp!””)); more appealing.
The benefit is that you only have to mixin once, whereas the usage does not require a mixin. It just goes next to your import statements. However, multiple scopes may make this less appealing, as you would have to mixin at any inner scope that has a variable you want to deal with. But I plan to write some string interpolation libraries based on this, would love to see a "better SQL" library for something like this. Not sure if it mitigates the need for an interpolation DIP, as clearly this is going to be compile-time intensive, where the cleverness is stomped on by memory usage and slow compile times. -Steve
Dec 10 2018
next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Monday, 10 December 2018 at 16:45:30 UTC, Steven Schveighoffer 
wrote:
 On 12/10/18 11:36 AM, Aliak wrote:
 On Monday, 10 December 2018 at 16:27:03 UTC, Steven 
 Schveighoffer wrote:
 On 12/10/18 11:11 AM, aliak wrote:
 This is much better than having to mixin everywhere. A 
 couple of things:

 1) Can this be put in a module so that you don't have to 
 mixin(enableInterpolation) but instead "import interp = 
 std.interpolation;" or something similar?
No, you need a local mixin. Doing that import just imports the *symbol* into your namespace, but it doesn't give access to your namespace to the symbol.
Au :(. Yeah that makes sense. Then I’m not sure I see how this improves things if it has to be mixed in to every scope you want to use interpolation for. The sparseness of interpolation might just make mixin(Interp!””)); more appealing.
The benefit is that you only have to mixin once, whereas the usage does not require a mixin. It just goes next to your import statements. However, multiple scopes may make this less appealing, as you would have to mixin at any inner scope that has a variable you want to deal with. But I plan to write some string interpolation libraries based on this, would love to see a "better SQL" library for something like this. Not sure if it mitigates the need for an interpolation DIP, as clearly this is going to be compile-time intensive, where the cleverness is stomped on by memory usage and slow compile times. -Steve
It can also have some pretty unexpected results. For example, having a variable reference an outerscope instance instead of the local one: import std.stdio; template somefun() { auto iterpolate(string s)() { //do some parsing return mixin(s[1 .. $]); } } enum enableInterpolate = "mixin somefun A; alias interpolate = A.iterpolate;"; mixin(enableInterpolate); enum a = 10; enum msg = iterpolate!("$a"); void main() { int a = 5; iterpolate!("$a").writeln; } This prints 10.
Dec 10 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/10/18 12:43 PM, Jonathan Marler wrote:

 It can also have some pretty unexpected results.  For example, having a 
 variable reference an outerscope instance instead of the local one:
Meh, I don't see this as really a problem or unexpected -- depending on how you look at the feature. In general, anyone who wants to use this technique would know to mixin the string in the function they are going to use it. I'm not saying the proposed DMD update and DIP isn't worth it, as even with the better call syntax, this is going to tax the CTFE engine hard, compared to what the compiler deals with (and that code already is there). But it's worth exploring how far we can get with this idea, to see the limitations and benefits. Maybe it solidifies the position that we need it in the language instead of the library. -Steve
Dec 10 2018
next sibling parent Jonathan Marler <johnnymarler gmail.com> writes:
On Monday, 10 December 2018 at 18:44:48 UTC, Steven Schveighoffer 
wrote:
 On 12/10/18 12:43 PM, Jonathan Marler wrote:

 It can also have some pretty unexpected results.  For example, 
 having a variable reference an outerscope instance instead of 
 the local one:
Meh, I don't see this as really a problem or unexpected -- depending on how you look at the feature. In general, anyone who wants to use this technique would know to mixin the string in the function they are going to use it. I'm not saying the proposed DMD update and DIP isn't worth it, as even with the better call syntax, this is going to tax the CTFE engine hard, compared to what the compiler deals with (and that code already is there). But it's worth exploring how far we can get with this idea, to see the limitations and benefits. Maybe it solidifies the position that we need it in the language instead of the library. -Steve
Yeah I agree we should explore this and try to find other options besides the new string literal syntax. But for me, this is reason enough not to use this technique in my code. It means that at any point when I see the "interpolate!(...)" call, I have to follow it back up to where mixin(enableInterpolate) appears and make sure it's in my scope and not an outer one. That's worse than making sure to free memory/resources every time you allocate them, and the amount of bugs that causes is bad enough. Any time your code depends on other code outside of itself you introduce more technical debt from the mental burden it takes to verify that code upon reading. Sometimes you can prevent that, but in this case I'd rather not use interpolation at all then introduce this kind of technical debt.
Dec 10 2018
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Dec 10, 2018 at 01:44:48PM -0500, Steven Schveighoffer via
Digitalmars-d wrote:
 On 12/10/18 12:43 PM, Jonathan Marler wrote:
 
 It can also have some pretty unexpected results. For example,
 having a variable reference an outerscope instance instead of the
 local one:
Meh, I don't see this as really a problem or unexpected -- depending on how you look at the feature. In general, anyone who wants to use this technique would know to mixin the string in the function they are going to use it. I'm not saying the proposed DMD update and DIP isn't worth it, as even with the better call syntax, this is going to tax the CTFE engine hard, compared to what the compiler deals with (and that code already is there). But it's worth exploring how far we can get with this idea, to see the limitations and benefits. Maybe it solidifies the position that we need it in the language instead of the library.
[...] Yes, pushing the limits of the current language will give us more solid arguments to convince W&A that language support is necessary. Otherwise there's a risk they will consider library solutions "good enough". T -- People tell me that I'm skeptical, but I don't believe them.
Dec 10 2018
prev sibling next sibling parent reply aliak <something something.com> writes:
On Monday, 10 December 2018 at 16:45:30 UTC, Steven Schveighoffer 
wrote:
 The benefit is that you only have to mixin once, whereas the 
 usage does not require a mixin. It just goes next to your 
 import statements.
Once per *every* function you want to use it in. Not sure I see the benefit actually 🤷‍♂️ Jonathan also brings up a very good point. Hello bugs. Cheers, - Ali
Dec 10 2018
parent reply Daniel Kozak <kozzi11 gmail.com> writes:
I do not see any problem with add one line in every function. Right now I
do much more of this with imports so I can live with that.
And there is a easy solution to fix Jonathan issue. Just do not use it on
global scope. You can force it to be used only on function scope and so on.

But obviosly it is still a hack and workaround about something what should
be in a language anyway.

On Mon, Dec 10, 2018 at 7:30 PM aliak via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Monday, 10 December 2018 at 16:45:30 UTC, Steven Schveighoffer
 wrote:
 The benefit is that you only have to mixin once, whereas the
 usage does not require a mixin. It just goes next to your
 import statements.
Once per *every* function you want to use it in. Not sure I see the benefit actually =F0=9F=A4=B7=E2=80=8D=E2=99=82=EF=B8=8F Jonathan als=
o brings up a very good
 point. Hello bugs.

 Cheers,
 - Ali
Dec 10 2018
next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
On Monday, 10 December 2018 at 18:35:16 UTC, Daniel Kozak wrote:
 I do not see any problem with add one line in every function. 
 Right now I
 do much more of this with imports so I can live with that.
 And there is a easy solution to fix Jonathan issue. Just do not 
 use it on
 global scope. You can force it to be used only on function 
 scope and so on.

 But obviosly it is still a hack and workaround about something 
 what should be in a language anyway.

 On Mon, Dec 10, 2018 at 7:30 PM aliak via Digitalmars-d < 
 digitalmars-d puremagic.com> wrote:

 On Monday, 10 December 2018 at 16:45:30 UTC, Steven 
 Schveighoffer wrote:
 The benefit is that you only have to mixin once, whereas the 
 usage does not require a mixin. It just goes next to your 
 import statements.
Once per *every* function you want to use it in. Not sure I see the benefit actually 🤷‍♂️ Jonathan also brings up a very good point. Hello bugs. Cheers, - Ali
It's not just global scope, it's any outer scope. Examples: class { mixin(enableInterpolate); // BAD void foo() { interpolate!(...) } } void foo2() { mixin(enableInterpolate); // BAD void inner_foo() { interpolate!(...) } }
Dec 10 2018
parent reply Daniel Kozak <kozzi11 gmail.com> writes:
On Mon, Dec 10, 2018 at 7:40 PM Jonathan Marler via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 It's not just global scope, it's any outer scope. Examples:

 class
 {
      mixin(enableInterpolate); // BAD
      void foo()
      {
          interpolate!(...)
      }
 }

 void foo2()
 {
      mixin(enableInterpolate); // BAD
      void inner_foo()
      {
          interpolate!(...)
      }
 }

 You can fix this too
https://gist.github.com/run-dlang/b797fd9e1d4993aeafcdb3d0523ef465
Dec 10 2018
parent Jonathan Marler <johnnymarler gmail.com> writes:
On Monday, 10 December 2018 at 19:04:21 UTC, Daniel Kozak wrote:
 On Mon, Dec 10, 2018 at 7:40 PM Jonathan Marler via 
 Digitalmars-d < digitalmars-d puremagic.com> wrote:

 It's not just global scope, it's any outer scope. Examples:

 class
 {
      mixin(enableInterpolate); // BAD
      void foo()
      {
          interpolate!(...)
      }
 }

 void foo2()
 {
      mixin(enableInterpolate); // BAD
      void inner_foo()
      {
          interpolate!(...)
      }
 }

 You can fix this too
https://gist.github.com/run-dlang/b797fd9e1d4993aeafcdb3d0523ef465
Yes that does mitigate the issue. It's a clever idea overall (someone else had something similar too). Not as elegant as the string literal syntax but it's clever. Would I use it in my code? Not by default, but if I had a case where string interpolation was very beneficial (i.e. code generation) then I might use it.
Dec 10 2018
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Mon, 10 Dec 2018 19:35:16 +0100, Daniel Kozak wrote:
 I do not see any problem with add one line in every function.
One line in every scope, rather: void foo() { mixin(interpSupport); { auto i = 10; // doesn't work writeln(interp!"i is $i"); } }
Dec 10 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/10/18 2:16 PM, Neia Neutuladh wrote:
 On Mon, 10 Dec 2018 19:35:16 +0100, Daniel Kozak wrote:
 I do not see any problem with add one line in every function.
One line in every scope, rather: void foo() { mixin(interpSupport); { auto i = 10; // doesn't work writeln(interp!"i is $i"); } }
This is less of a problem, because it breaks and tells you why it breaks. Whereas the other silent hijacking of which symbol you're talking about could be more confusing. People would likely just hoist those variables up to the outer scope rather than moving the mixin (I know I would). Still one of the warts, though. -Steve
Dec 10 2018
prev sibling parent Kagamin <spam here.lot> writes:
On Monday, 10 December 2018 at 16:45:30 UTC, Steven Schveighoffer 
wrote:
 However, multiple scopes may make this less appealing, as you 
 would have to mixin at any inner scope that has a variable you 
 want to deal with.
Was mangling of nested functions fixed?
Dec 12 2018
prev sibling next sibling parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 10 December 2018 at 10:11:44 UTC, Daniel Kozak wrote:
 https://run.dlang.io/is/FbOnGI

 import std.stdio;
 template somefun()
 {
      auto iterpolate(string s)()
      {
          //do some parsing
          return mixin(s[1 .. $]);
      }
 }

 enum enableInterpolate = "mixin somefun A; alias interpolate = 
 A.iterpolate;";

 void main()
 {
     mixin(enableInterpolate);
     int a = 5;
     iterpolate!("$a").writeln;
 }
Nice.
Dec 10 2018
prev sibling next sibling parent Martin Tschierschke <mt smartdolphin.de> writes:
On Monday, 10 December 2018 at 10:11:44 UTC, Daniel Kozak wrote:
 https://run.dlang.io/is/FbOnGI

 import std.stdio;
 template somefun()
 {
      auto iterpolate(string s)()
      {
          //do some parsing
          return mixin(s[1 .. $]);
      }
 }

 enum enableInterpolate = "mixin somefun A; alias interpolate = 
 A.iterpolate;";

 void main()
 {
     mixin(enableInterpolate);
     int a = 5;
     iterpolate!("$a").writeln;
 }
It is similar to my idea: https://forum.dlang.org/post/uwbwjlmphpkllpeojjno forum.dlang.org (Where I define two functions mixinter(String)() and exho(string)() to get a shorter expression when using scriptlike (dub package).) So, yes very good :-)
Dec 10 2018
prev sibling parent 12345swordy <alexanderheistermann gmail.com> writes:
On Monday, 10 December 2018 at 10:11:44 UTC, Daniel Kozak wrote:
 https://run.dlang.io/is/FbOnGI

 import std.stdio;
 template somefun()
 {
      auto iterpolate(string s)()
      {
          //do some parsing
          return mixin(s[1 .. $]);
      }
 }

 enum enableInterpolate = "mixin somefun A; alias interpolate = 
 A.iterpolate;";

 void main()
 {
     mixin(enableInterpolate);
     int a = 5;
     iterpolate!("$a").writeln;
 }
The issue that I have with this is that users do not want to manually add the mixin to the code scope every time they want string interpolation and worry about bugs that they may run across due to the misuse of the mixin. Let the compiler do the heavy lifting.
Dec 10 2018