On Friday, 6 April 2018 at 20:33:10 UTC, Chris Katko wrote:
Sorry if this is "re-opening" an old thread, but did anything come from this and DIP50? It seems like a really interesting concept and this thread was one of the first results for a Google search.

Thanks.

Thanks for reminding me about this thread, I thought I would see how close i could get to having this work now that I know D's edges better:

On Sunday, 11 July 2010 at 13:29:36 UTC, Michel Fortin wrote:

That said, I don't feel like I'm cheating when using string mixins. I find them a quite good substitute to AST macros. And perhaps string mixins are simpler too: you don't have to learn a new macro syntax, you just manipulate strings. Though I'm still waiting for the day we can use string mixins in expressions to do things like this:

        int num = 1;
        string result = substitute!"Number: $num";
        assert(result == "Number: 1");

So what i came up with was:

--- substituter.d
module substituter;

string substituteForMixin(string input){

    input ~= ` `;
    string output = ``;
    size_t l= input.length-1;

    for(size_t i=0; i<l; i++){
        if(input[i]=='#' && input[i+1]=='{'){
            output ~= (i==0) ? `to!string(` : `"~to!string(`;
            i+=2;
            while(input[i] != '}'){
                output ~= input[i++];
            }
            output ~= (i==l-1) ? `)` : `)~"`;
        }else{
            if(i==0) output ~= `"`;
            output ~= input[i];
            if(i==l-1) output ~= `"`;
        }
    }
    return output;
}

mixin template Substituter(){
    string substitute(alias string ident)(){
        import std.conv : to;
        mixin(`return `~substituteForMixin(ident)~`;`);
    };
}

@safe unittest{

    mixin Substituter;

    int number = 42;

    assert(
        substitute!("number is #{number}")
        == "number is 42"
    );
    assert(
        substitute!("#{number+1} is the result of number+1")
        == "43 is the result of number+1"
    );
    assert(
substitute!("you get #{number*number} when you square number")
        == "you get 1764 when you square number"
    );
}
---

I think thats as clean as I can make it currently. Also I'm using #{} syntax to allow basic expressions and converting to!string on everything.

Not sure if this syntax is documented but you can use enum like:

enum wrapmixin(alias string s) = {mixin(s)};
enum wrapmixin(alias string s){mixin(s)};

And the mixin() is run at the time the enum is called. The only problem with this though is the lambda/function is defined in the scope enum was defined not where the enum was called. So I think its just sugar for:

template wrapmixin(alias string s){alias wrapmixin = {mixin(s);}}
template wrapmixin(alias string s){auto wrapmixin(){mixin(s);}}

To work around this I used a mixin template to work around the scope issue.

Now to bring us back to 2018, I think that this enum version of the code should inject the function/lambda into the scope of it's caller. That would allow us to do a lot of cool things.

Also the other idea I had was to have mixin functions that only take compiletime args (they are UFCS-able though, unlike templates) and mixin themselves when called like:

---
mixin add1Xtimes(alias int a, alias int t){
    uint i=t;
    do{a++;}while(--i);
    return a;
}
@safe unittest{
    uint n=0;
    n.add1Xtimes!(4).add1Xtimes!(6);
    assert(n==10);
}
---

And that becomes:

---
@safe unittest{
    uint n=0;
    uint __m0_i=4;
    do{n++;}while(--__m0_i);
    uint __m1_i=6;
    do{n++;}while(--__m1_i);
    assert(n==10);
}
---

Notice that if the return is never used it's stripped out.

I haven't really given much thought to the implementation of this though.

Reply via email to