www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Transform a function's body into a string for mixing in

reply Emmanuelle <VuLXn6DBW PPtUm7TvV6nsw.com> writes:
Hello!

Is there any trait or Phobos function for transforming a 
function/delegate/lambda/whatever's body into a string suitable 
for `mixin(...)`? For example:

---
__traits(getBody, (int a, int b) => a + b); // returns "(int a, 
int b) => a + b"
                                             // or maybe just "a + 
b"
---

If not, is there any way to do this _without_ using strings? They 
are very inconvenient and could hide errors.

Thanks!
Jun 20 2019
next sibling parent Max Haughton <maxhaton gmail.com> writes:
On Thursday, 20 June 2019 at 19:09:11 UTC, Emmanuelle wrote:
 Hello!

 Is there any trait or Phobos function for transforming a 
 function/delegate/lambda/whatever's body into a string suitable 
 for `mixin(...)`? For example:

 ---
 __traits(getBody, (int a, int b) => a + b); // returns "(int a, 
 int b) => a + b"
                                             // or maybe just "a 
 + b"
 ---

 If not, is there any way to do this _without_ using strings? 
 They are very inconvenient and could hide errors.

 Thanks!
We don't have anything AST-macro ish or a trait as described. The trait isn't impossible to implement but I could imagine it being a nightmare for compile times
Jun 20 2019
prev sibling parent reply Dennis <dkorpel gmail.com> writes:
On Thursday, 20 June 2019 at 19:09:11 UTC, Emmanuelle wrote:
 Is there any trait or Phobos function for transforming a 
 function/delegate/lambda/whatever's body into a string suitable 
 for `mixin(...)`? For example:
See: https://forum.dlang.org/post/kozwskltzidfnatbpjgb forum.dlang.org
 If not, is there any way to do this _without_ using strings?
Depends on what you are trying to achieve with mixing in function body code. If you just want to execute the function code, you can just call it (obviously), so I assume you want dynamic scoping (that global variables are overridden by local variables from the caller) or something?
Jun 20 2019
parent reply Emmanuelle <VuLXn6DBW PPtUm7TvV6nsw.com> writes:
On Thursday, 20 June 2019 at 20:38:48 UTC, Dennis wrote:
 On Thursday, 20 June 2019 at 19:09:11 UTC, Emmanuelle wrote:
 Is there any trait or Phobos function for transforming a 
 function/delegate/lambda/whatever's body into a string 
 suitable for `mixin(...)`? For example:
See: https://forum.dlang.org/post/kozwskltzidfnatbpjgb forum.dlang.org
 If not, is there any way to do this _without_ using strings?
Depends on what you are trying to achieve with mixing in function body code. If you just want to execute the function code, you can just call it (obviously), so I assume you want dynamic scoping (that global variables are overridden by local variables from the caller) or something?
Yeah, I want to be able to basically use mixin templates but with expressions instead, with the code being executed on the scope of the caller, not the callee; but it seems that's impossible without passing strings. For example, I recently hit an issue with closure scoping (https://forum.dlang.org/post/rnxebjcfpmyzptpwzyee forum.dlang.org) that can be worked around by using IIFEs; I thought, hey, maybe I could make a mixin that turns, say, this (taking the example from the post I just linked): --- ((x) => (int i) { nums[x] ~= i; })(x); --- into this: --- mixin(capture!(x, (int i) { nums[x] ~= i; }); --- where the variables I need captured go first there (in this case, only `x`). Of course, that doesn't work unless I use strings everywhere: --- mixin(capture!("x", q{(int i) { nums[x] ~= i; }}); --- which I find rather ugly and inconvenient. The technique you linked seems interesting but also loads of work so I'll just give up on this idea for now lol. Thanks though!
Jun 21 2019
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Friday, 21 June 2019 at 15:42:56 UTC, Emmanuelle wrote:
 Yeah, I want to be able to basically use mixin templates but 
 with expressions instead, with the code being executed on the 
 scope of the caller, not the callee; but it seems that's 
 impossible without passing strings
This sounds very similar to something I hacked together a while ago and recently wrote about making cleaner code: https://forum.dlang.org/post/ekbyseslunvmudkhlhoh forum.dlang.org The idea here was to do a pass-by-value lambda. Usage: --- auto bar(T)(T x) nogc { return x(10); } auto foo(int x) nogc { auto f = lambda!(x, q{ (int y) { return x + y; } }); return f; } void main() { import std.stdio; writeln(foo(15)(10)); } --- Magic implementation: --- template lambda(Args...) { static struct anon { static foreach(i; 0 .. Args.length - 1) mixin("typeof(Args[i]) " ~ __traits(identifier, Args[i]) ~ ";"); auto opCall(T...)(T t) { return mixin(Args[$-1])(t); } this(T...)(T t) { this.tupleof = t; } } anon lambda() { anon a; // copy the values in a.tupleof = Args[0 .. $-1]; return a; } } --- You could convert that into a regular delegate too, so it works with non-template consumers, by generating a non-templated opCall and taking its address.
Jun 21 2019
parent Emmanuelle <VuLXn6DBW PPtUm7TvV6nsw.com> writes:
On Friday, 21 June 2019 at 15:54:35 UTC, Adam D. Ruppe wrote:
 On Friday, 21 June 2019 at 15:42:56 UTC, Emmanuelle wrote:
 [...]
This sounds very similar to something I hacked together a while ago and recently wrote about making cleaner code: https://forum.dlang.org/post/ekbyseslunvmudkhlhoh forum.dlang.org The idea here was to do a pass-by-value lambda. Usage: --- auto bar(T)(T x) nogc { return x(10); } auto foo(int x) nogc { auto f = lambda!(x, q{ (int y) { return x + y; } }); return f; } void main() { import std.stdio; writeln(foo(15)(10)); } --- Magic implementation: --- template lambda(Args...) { static struct anon { static foreach(i; 0 .. Args.length - 1) mixin("typeof(Args[i]) " ~ __traits(identifier, Args[i]) ~ ";"); auto opCall(T...)(T t) { return mixin(Args[$-1])(t); } this(T...)(T t) { this.tupleof = t; } } anon lambda() { anon a; // copy the values in a.tupleof = Args[0 .. $-1]; return a; } } --- You could convert that into a regular delegate too, so it works with non-template consumers, by generating a non-templated opCall and taking its address.
Oh wow, that's pretty awesome. Too bad it seems the token string around the lambda is unavoidable, but it's not a big deal I think. In any case thanks, you snippet helped me figure out the template I wanted without overuse of strings!
Jun 21 2019