March 22, 2012
On Wednesday, 21 March 2012 at 15:11:47 UTC, Andrei Alexandrescu wrote:
>> class Foo
>> {
>> int a;
>> int b;
>>
>> mixin NonSerialized!(b);
>> }
>
> I think the liability here is that b needs to appear in two places, once in the declaration proper and then in the NonSerialized part. (A possible advantage is that sometimes it may be advantageous to keep all symbols with a specific attribute in one place.) A possibility would be to make the mixin expand to the field and the metadata at once.
>

In case my proof of concept which was posted in another thread was overlooked... it was my goal to address this very issue... also it's possible to change the datatype of the members in the "parallel annotation class"(Foo_Serializable in my limited example), and store any extra user data there if so desired while keeping the real class clean... and then simply traverse with __traits.

import std.stdio;
import std.array;
import std.string;
import std.algorithm;

string attr(string complex_decl)
{
  string org_struct;
  string ser_struct;

  auto lines = splitLines(complex_decl);

  {
    auto decl = split(stripLeft(lines[0]));

    if(decl[0]=="struct")
    {
      org_struct = decl[0] ~ " " ~ decl[1];
      ser_struct = decl[0] ~ " " ~ decl[1] ~ "_Serializable";
    }
    else
      return complex_decl;
  }

  foreach(line; lines[1..$])
  {
    auto attr = findSplitAfter(stripLeft(line), "@NonSerialized ");

    if(attr[0]=="@NonSerialized ")
      org_struct ~= attr[1];
    else
    {
      org_struct ~= attr[1];
      ser_struct ~= attr[1];
    }
  }

  return ser_struct ~ "\n" ~ org_struct;
}

mixin(attr(q{struct Foo
{
  @NonSerialized int x;
  @NonSerialized int y;
  int z;
}}));

void main()
{
  auto m = [ __traits(allMembers, Foo) ];
  writeln("Normal members of Foo:", m);

  auto n = [ __traits(allMembers, Foo_Serializable) ];
  writeln("Serializable members of Foo:", n);
}

March 22, 2012
On 3/21/12 6:02 PM, deadalnix wrote:
> Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :
>> On 3/21/12 11:17 AM, Timon Gehr wrote:
>>> On 03/20/2012 10:36 PM, deadalnix wrote:
>>>> Even the propagation of pure, @safe, nothrow and const that has been
>>>> discussed recently can be done with that feature.
>>>>
>>>
>>> I'm sceptical. How would that work exactly?
>>
>> I, too, am highly skeptical. For one thing these attributes must be made
>> part of the type and have deep connections with code semantics.
>>
>> Andrei
>
> That is the point. The property must be able to manipulate what is
> qualified. This is the point of AOP.

Problem is you'd need a ton of hooks to e.g. prevent mutation in const methods, or do attribute inference as the compiler does now.

> If you let me some time, I could write a proposal. BTW, this is probably
> something we don't want to rush in, but I'm sure it definitively worth it.

Honest, I have zero enthusiasm for adding AOP to D right now. But I agree it would be an interesting project to see what it would take to implement something of the power of pure or const starting from zero knowledge.

> Have you seen what a project like lombok can do ?

This? http://projectlombok.org/ I'll take a look, thanks.


Andrei
March 22, 2012
On 21 March 2012 20:32, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote:

> Well if the argument boils down to nice vs. ugly, as opposed to possible vs. impossible - it's quite a bit less compelling.
>

By this logic, I might as well stick with C++. It's 'possible' to do
everything I need, but it makes my cry myself to sleep at night, and wastes
insane amounts of time.
Code simplicity and cleanliess on the front end IS important, it makes code
reasable, maintainable.
In a large code team, if someone has to go out of their way to understand
some code, chances are, they won't understand it, and make improper or
damaging changes to it. Or make improper implementations based on it.
This will destroy your code across 5 years or so. In gamedev, the average
engine codebase lasts 10+ years.


March 22, 2012
On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
> I think the liability here is that b needs to appear in two places.

Andrei, how about this:

import std.stdio;
import std.conv;

struct NonSerialized(T)
{
    enum isNonSerialized = true;
    T payload;
    alias payload this;
}

struct Foo
{
    this(int x, string name, string lastName, string extra) {
        this.x = x;
        this.name = name;
        this.lastName = lastName;
        this.extra = extra;
    }

    int x;
    NonSerialized!string name;
    NonSerialized!string lastName;
    string extra;
}

string serialize(T)(T input)
{
    string result;

    foreach (i, field; input.tupleof)
    {
        static if (skipSerialize!(typeof(field)))
            result ~= to!string(typeof(field).init) ~ "\n";
        else
            result ~= to!string(field) ~ "\n";
    }

    return result;
}

template skipSerialize(T)
{
    enum bool skipSerialize = hasValidMember!(T, "isNonSerialized");
}

template hasValidMember(T, string member)
{
    static if (__traits(hasMember, T, member))
    {
        enum bool hasValidMember = mixin("T." ~ member);
    }
    else
        enum bool hasValidMember = false;
}


void main()
{
    Foo foo = Foo(10, "Foo", "Bar", "Doo");
    string bin = serialize(foo);
    writeln(bin);
}

Note that I've had to make a constructor because I can't implicitly assign a string to a NonSerialized!string inside of a struct literal (but I think this is just a DMD frontend bug).
March 22, 2012
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:
> On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>> I think the liability here is that b needs to appear in two places.
>
> Andrei, how about this:
>
> Note that I've had to make a constructor because I can't implicitly
> assign a string to a NonSerialized!string inside of a struct literal
> (but I think this is just a DMD frontend bug).

Now, this is pure beauty. :)
March 22, 2012
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:
> On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:
>> I think the liability here is that b needs to appear in two places.
>
> Andrei, how about this:
>
>
> Note that I've had to make a constructor because I can't implicitly
> assign a string to a NonSerialized!string inside of a struct literal
> (but I think this is just a DMD frontend bug).

Altering the type is not a reasonable approach for a generic annotation system.
1) It wrecks things that use template arguments, possibly special casing for certain types.
2) It makes it difficult to combine attributes.
3) It forces altering actual code to indicate an annotation, as opposed to just annotating it.

Honestly, C# already handles annotations perfectly (with the exception of too little compile-time power to specify the arguments), I see no reason to attempt to reinvent it. I've never had a problem where I thought that C#'s attributes were not sufficient.
March 22, 2012
On 20/03/12 22:29, Jacob Carlborg wrote:
> On 2012-03-20 17:13, Andrei Alexandrescu wrote:
>> On 3/20/12 10:52 AM, Jacob Carlborg wrote:
>>> On 2012-03-20 16:17, Andrei Alexandrescu wrote:
>>>> On 3/20/12 12:50 AM, Kapps wrote:
>>>
>>>> Perhaps we should add a field of type Variant[string].
>>>
>>> No, not Variant[string] again.
>>
>> Why? I thought it was agreed that that's a good idea for exceptions.
>
> Maybe you agreed on that. I still don't like it.

I'm not sure who agreed, but I don't even think it would work. What happens if the AA or Variant throws an exception? Especially if AAs become a library type, they've got no business being in something as fundamental as an exception base class.

It leads to a big ball of mud design.
March 22, 2012
On 3/22/12, Kapps <opantm2+spam@gmail.com> wrote:
> 1) It wrecks things that use template arguments, possibly special casing for certain types.

Yeah I've noticed things tend to break with alias this. Anyway if templates could be improved for these edge-cases then it's a benefit for everyone, regardless of annotations. :)

> 2) It makes it difficult to combine attributes.

Difficult.. well maybe. You could use something like: Attribute!(int, NonSerialized, Encrypted) ID1, ID2;

Or override opAssign and use bit masks:

Attribute!int ID1 = NonSerialized | Encrypted; Attribute!int ID2 = NonSerialized | Encrypted;

With attributes this would maybe be:
@NonSerialized @Encrypted int ID1, ID2;

I do like the attribute syntax more.

> 3) It forces altering actual code to indicate an annotation, as opposed to just annotating it.

Yup.
March 22, 2012
On 3/22/12 2:32 AM, Andrei Alexandrescu wrote:
> On 3/21/12 12:06 PM, Jacob Carlborg wrote:
>> On 2012-03-21 16:11, Andrei Alexandrescu wrote:
>> I think the liability here is that b needs to appear in two places, once
>>> in the declaration proper and then in the NonSerialized part. (A
>>> possible advantage is that sometimes it may be advantageous to keep all
>>> symbols with a specific attribute in one place.) A possibility would be
>>> to make the mixin expand to the field and the metadata at once.
>>
>> Yes, but that just looks ugly:
>>
>> class Foo
>> {
>> int a;
>> mixin NonSerialized!(int, "b");
>> }
>>
>> That's why it's so nice with attributes.
>
> Well if the argument boils down to nice vs. ugly, as opposed to possible
> vs. impossible - it's quite a bit less compelling.
>
> Andrei

Why don't you program everything with gotos instead of for, foreach and while? If it boils down to nice vs. ugly, as opposed to possible vs. impossible...

Hmm...
March 22, 2012
On 03/21/12 14:36, Adam D. Ruppe wrote:
> On Wednesday, 21 March 2012 at 08:29:23 UTC, Tove wrote:
>> With the mixin improvement proposal any arbitrarily complex feature can be implemented in the library, appearing to enjoy first class syntax with just 1 extra character penalty vs the compiler.
> 
> My main concern with the library implementation isn't syntax.
> 
> The big question is still: where will you put the annotation data?

A (new) compile-time only (mutable) storage class.
It's probably needed for other things too, to avoid compiler-specific
pragmas+section-attributes+linker-magic to achieve the same effect.

For run-time accessible custom attributes (if those are even necessary), the lib solution wouldn't be much different than a built-in one anyway.

artur