Thread overview
Passing variables, preserving UDAs: A Gripe
Feb 07, 2017
Nick Sabalausky
Feb 08, 2017
Timon Gehr
Feb 08, 2017
John Colvin
Feb 12, 2017
Timon Gehr
Feb 08, 2017
Kagamin
Feb 08, 2017
Nick Sabalausky
Feb 08, 2017
Nick Sabalausky
February 07, 2017
Suppose I have some code that operates on a variable's value and its UDAs. And I want to refactor that code into a reusable function. Sounds simple enough, right?

So, consider a basic example:

----------------------------
class Foo
{
    @("Hello")
    string s;
}

void doStuff(alias var)()
{
    var = "abc";

    import std.traits;
    assert(hasUDA!(var, "Hello") == true);
}

void main()
{
    @("Hello")
    string s;
    doStuff!(s);

    auto foo = new Foo();
    // Error: need 'this' for 'doStuff' of type 'pure nothrow @nogc @safe void()'
    doStuff!(foo.s);
}
----------------------------

Note the error. Naturally, that cannot compile, because you can't instantiate a template based on the value of a variable at runtime (ie, based on the value of `foo`).

This can be made to *compile* if you pass by runtime ref instead of alias:

----------------------------
void doStuff(T)(ref T var)
{
    var = "abc";

    import std.traits;
    assert(hasUDA!(var, "Hello") == true); // Fail!
}

void main()
{
    auto foo = new Foo();
    doStuff(foo.s); // Ok
}
----------------------------

But as expected, the UDAs are not preserved because UDAs are attached to declarations, not values.

This CAN be made to work, albeit very awkwardly:

----------------------------
class Foo
{
    @("Hello")
    string s;
}

void doStuff(alias var)()
{
    var = "abc";

    import std.traits;
    assert(hasUDA!(var, "Hello") == true);
}

void doStuffMember(string memberName, ObjType)(ObjType obj)
{
    __traits(getMember, obj, memberName) = "abc";

    import std.traits;
    assert(hasUDA!(__traits(getMember, obj, memberName), "Hello") == true);
}

void main()
{
    @("Hello")
    string s;
    doStuff!(s);

    auto foo = new Foo();
    doStuffMember!("s")(foo);
}
----------------------------

But now it's:

1. A complete freaking mess

2. An unintuitively inconsistent interface

3. A blatant DRY violation

4. AFAICS, cannot be DRY-ed up particularly well without either running into the original problem, resorting to string mixins (which comes with its own problems), or saying "to hell with using D's UDA interfaces within my function" and just passing the result of getUDAs into the function to be used instead, and recreating stuff like hasUDA to operate on the results of getUDAs instead of the symbols directly.

5. Did I mention it's A COMPLETE FREAKING MESS for what seems like a very simple problem?

February 08, 2017
On 07.02.2017 22:59, Nick Sabalausky wrote:
> Suppose I have some code that operates on a variable's value and its
> UDAs. And I want to refactor that code into a reusable function. Sounds
> simple enough, right?
>
> So, consider a basic example:
>
> ----------------------------
> class Foo
> {
>     @("Hello")
>     string s;
> }
>
> void doStuff(alias var)()
> {
>     var = "abc";
>
>     import std.traits;
>     assert(hasUDA!(var, "Hello") == true);
> }
>
> void main()
> {
>     @("Hello")
>     string s;
>     doStuff!(s);
>
>     auto foo = new Foo();
>     // Error: need 'this' for 'doStuff' of type 'pure nothrow @nogc
> @safe void()'
>     doStuff!(foo.s);
> }
> ----------------------------
>
> Note the error. Naturally, that cannot compile, because you can't
> instantiate a template based on the value of a variable at runtime (ie,
> based on the value of `foo`).

It actually can compile. (It just doesn't.)
There is no essential difference between the two cases.
February 08, 2017
try this:
void main()
{
    auto foo = new Foo();
    doStuff!(Foo.s);
}
February 08, 2017
On Wednesday, 8 February 2017 at 07:57:15 UTC, Timon Gehr wrote:
> On 07.02.2017 22:59, Nick Sabalausky wrote:
>> Suppose I have some code that operates on a variable's value and its
>> UDAs. And I want to refactor that code into a reusable function. Sounds
>> simple enough, right?
>>
>> So, consider a basic example:
>>
>> ----------------------------
>> class Foo
>> {
>>     @("Hello")
>>     string s;
>> }
>>
>> void doStuff(alias var)()
>> {
>>     var = "abc";
>>
>>     import std.traits;
>>     assert(hasUDA!(var, "Hello") == true);
>> }
>>
>> void main()
>> {
>>     @("Hello")
>>     string s;
>>     doStuff!(s);
>>
>>     auto foo = new Foo();
>>     // Error: need 'this' for 'doStuff' of type 'pure nothrow @nogc
>> @safe void()'
>>     doStuff!(foo.s);
>> }
>> ----------------------------
>>
>> Note the error. Naturally, that cannot compile, because you can't
>> instantiate a template based on the value of a variable at runtime (ie,
>> based on the value of `foo`).
>
> It actually can compile. (It just doesn't.)
> There is no essential difference between the two cases.

How much work is it likely to be to make this happen in dmd? I imagine your frontend can do this already, but that's not a practical solution even for the medium term.
February 08, 2017
On 02/08/2017 07:38 AM, Kagamin wrote:
> try this:
> void main()
> {
>      auto foo = new Foo();
>      doStuff!(Foo.s);
> }

Same result:
Error: need 'this' for 'doStuff' of type 'pure nothrow @nogc @safe void()'

But even if that did compile, it still wouldn't work because doStuff wouldn't be able to access "foo.s" since "foo" isn't getting passed in in any way.

February 08, 2017
On 02/08/2017 01:00 PM, Nick Sabalausky wrote:
>
> But even if that did compile, it still wouldn't work because doStuff
> wouldn't be able to access "foo.s" since "foo" isn't getting passed in
> in any way.
>

I mean, in order to *set* a value for "foo.s", not *just* access UDAs.

February 12, 2017
On 08.02.2017 14:09, John Colvin wrote:
> On Wednesday, 8 February 2017 at 07:57:15 UTC, Timon Gehr wrote:
>> On 07.02.2017 22:59, Nick Sabalausky wrote:
>>> Suppose I have some code that operates on a variable's value and its
>>> UDAs. And I want to refactor that code into a reusable function. Sounds
>>> simple enough, right?
>>>
>>> So, consider a basic example:
>>>
>>> ----------------------------
>>> class Foo
>>> {
>>>     @("Hello")
>>>     string s;
>>> }
>>>
>>> void doStuff(alias var)()
>>> {
>>>     var = "abc";
>>>
>>>     import std.traits;
>>>     assert(hasUDA!(var, "Hello") == true);
>>> }
>>>
>>> void main()
>>> {
>>>     @("Hello")
>>>     string s;
>>>     doStuff!(s);
>>>
>>>     auto foo = new Foo();
>>>     // Error: need 'this' for 'doStuff' of type 'pure nothrow @nogc
>>> @safe void()'
>>>     doStuff!(foo.s);
>>> }
>>> ----------------------------
>>>
>>> Note the error. Naturally, that cannot compile, because you can't
>>> instantiate a template based on the value of a variable at runtime (ie,
>>> based on the value of `foo`).
>>
>> It actually can compile. (It just doesn't.)
>> There is no essential difference between the two cases.
>
> How much work is it likely to be to make this happen in dmd?

The problem is this:

struct S{
    int x;
}
void main(){
    S s;
    alias y=s.x; // silently ignores the 'this' expression, uses S.x
    y=4; // error, no this
}

I have brought this up before. The answer was: "Alias declarations are for symbols, not expressions."

The 'symbol' term should be generalized to include the case of base symbol together with an access path. I don't think this is very hard to do, but I don't know how much of DMDs codebase depends on the semantics being what they are.


> I imagine your frontend can do this already,

Your imagination is correct.


> but that's not a practical solution
> even for the medium term.

The frontends should converge to a common language anyway.