November 06, 2012
n 11/6/2012 12:59 AM, Jakob Ovrum wrote:
> Problem is that there's no way to do this without having the user specify which
> modules it should work for, like:
>
> import attributes;
> import a, b, c;
>
> static this() // This code cannot be automated.
> {
>      initAttributes!a();
>      initAttributes!b();
>      initAttributes!c();
> }
>

Is that really a problem?
November 06, 2012
On 11/6/2012 1:06 AM, Jonas Drewsen wrote:
> Wow! This is an early christmas gift.
>
> Just to be sure. Is the following also possible:
>
> ["Serializable"]
> class Foo
> {
>     int bar;
> }
>
> This will attach the UDA to the ClassDeclaration.
>
> The examples you've shown only attach UDAs to variable declarations.
>
> /Jonas
>

Yes, you can attach them to other symbols, including user defined types.
November 06, 2012
On Tuesday, 6 November 2012 at 09:08:25 UTC, Walter Bright wrote:
> On 11/6/2012 1:06 AM, Jonas Drewsen wrote:
>> Wow! This is an early christmas gift.
>>
>> Just to be sure. Is the following also possible:
>>
>> ["Serializable"]
>> class Foo
>> {
>>    int bar;
>> }
>>
>> This will attach the UDA to the ClassDeclaration.
>>
>> The examples you've shown only attach UDAs to variable declarations.
>>
>> /Jonas
>>
>
> Yes, you can attach them to other symbols, including user defined types.

Great. Can't wait to play around with this. I think project Orange could really take advantage of this.

/Jonas

November 06, 2012
Am 06.11.2012 09:39, schrieb Jakob Ovrum:
> On Tuesday, 6 November 2012 at 07:55:51 UTC, Walter Bright wrote:
>> -snip-
> 
> It doesn't look like it would be possible to schedule any runtime code using this, meaning they're not usable for stuff like registering types for serialization support, or doing runtime linking when attached to a function pointer, etc.
> 
> Even with user defined types or functions used in the attribute, the type or function itself doesn't know anything about the symbol it is being attached to, so it's quite limited what it can do.
> 
> Compared to other prevalent UDA systems, like C#'s, I'd say that this one is extremely limiting due to the two above points.
> 
> I'd love to hear some examples of what this particular take on UDAs allows in D, though.
> 

I see this as a feature in a way. The way C# attributes are sometimes used can feel a lot like magic, because they can do things on their own. In contrast it feels quite clean to me to simply think of attributes of what the word means - a simple tag on the declaration.

Some otherwise possible globally operating applications may not be doable without some boilerplate code. But a lot of stuff, including controlling seralization, interface generation or statically checked custom type constraints should be perfectly possible.
November 06, 2012
Walter Bright wrote:
> References:
> 
> http://www.digitalmars.com/d/archives/digitalmars/D/Custom_attributes_again_163042.html
> 
> http://www.digitalmars.com/d/archives/digitalmars/D/custom_attribute_proposal_yeah_another_one_163246.html
> 
> Inspired by a gallon of coffee, I decided to get it implemented. It's simple, based on what D already does (CTFE and heterogeneous tuples), easy to implement, easy to understand, and doesn't break anything. It should do everything asked for in the above references (except it's not a type constructor).
> 
> You can download it here and try it out:
> 
> http://ftp.digitalmars.com/dmd2beta.zip
> 
> As a bonus, that beta also can generate Win64 executables, and you can even symbolically debug them with VS! (Thanks to Rainer Schütze for his invaluable help with that).
> 
> Here's the rather skimpy and lame spec I banged out:
> =====================================================
> User Defined Attributes
> -----------------------
> 
> User Defined Attributes (UDA) are compile time expressions that can be attached to a declaration. These attributes can then be queried, extracted, and manipulated at compile time. There is no runtime component to them.
> 
> Grammatically, a UDA is a StorageClass:
> 
>     StorageClass:
> 	UserDefinedAttribute
> 
>     UserDefinedAttribute:
>         [ ArgumentList ]
> 
> And looks like:
> 
>     [ 3 ] int a;
>     [ "string", 7 ]: int b;
> 
> If there are multiple UDAs in scope for a declaration, they are concatenated:
> 
>     [ 1 ] {
>        [ 2 ] int a;        // has UDA's [1,2]
>        [ "string" ] int b; // has UDA's [1,"string"]
>     }
> 
> UDA's can be extracted into an expression tuple using __traits:
> 
>     [ 'c' ] string s;
>     pragma(msg, __traits(getAttributes, s));
> 
> prints:
> 
>     tuple('c')
> 
> If there are no user defined attributes for the symbol, an empty tuple is returned. The expression tuple can be turned into a manipulatable tuple:
> 
>   template Tuple(T...) {
>     alias T Tuple;
>   }
> 
>   enum EEE = 7;
>   ["hello"] struct SSS { }
>   [3] { [4][EEE][SSS] int foo; }
> 
>   alias Tuple!(__traits(getAttributes, foo)) TP;
> 
>   pragma(msg, TP);
>   pragma(msg, TP[2]);
> 
> prints:
> 
>   tuple(3,4,7,(SSS))
>   7
> 
> and of course the tuple types can be used to declare things:
> 
>   TP[3] a;    // a is declared as an SSS
> 
> The attribute of the type name is not the same as the attribute of the variable:
> 
>   pragma(msg, __traits(getAttributes, typeof(a));
> 
> prints:
> 
>     tuple("hello")
> 
> Of course, the real value of UDA's is to be able to create user defined types with
> specific values. Having attribute values of basic types does not scale.
> The attribute tuples can be manipulated like any other tuple, and
> can be passed as the argument list to a template.
> 
> Whether the attributes are values or types is up to the user, and whether later
> attributes accumulate or override earlier ones is also up to how the
> user interprets them.

I wonder what are the benefits over a library solution. Something like

struct UserDefinedAttribute(Args...)
{
	alias Args[0 .. $ -1] attributes;
}

unittest
{
	UserDefinedAttribute!("my attr", int) a;
	UserDefinedAttribute!("my attr", 4, int) b;

	import std.stdio;
	writeln(typeof(a).attributes.stringof);
	writeln(typeof(b).attributes.stringof);
}

which admittedly has less syntactical appeal (and probably other
problems) but can maybe improved. But to which point.
What do you gain by adding it to the core language?

Jens
November 06, 2012
Jens Mueller wrote:
> Walter Bright wrote:
> > References:
> > 
> > http://www.digitalmars.com/d/archives/digitalmars/D/Custom_attributes_again_163042.html
> > 
> > http://www.digitalmars.com/d/archives/digitalmars/D/custom_attribute_proposal_yeah_another_one_163246.html
> > 
> > Inspired by a gallon of coffee, I decided to get it implemented. It's simple, based on what D already does (CTFE and heterogeneous tuples), easy to implement, easy to understand, and doesn't break anything. It should do everything asked for in the above references (except it's not a type constructor).
> > 
> > You can download it here and try it out:
> > 
> > http://ftp.digitalmars.com/dmd2beta.zip
> > 
> > As a bonus, that beta also can generate Win64 executables, and you can even symbolically debug them with VS! (Thanks to Rainer Schütze for his invaluable help with that).
> > 
> > Here's the rather skimpy and lame spec I banged out:
> > =====================================================
> > User Defined Attributes
> > -----------------------
> > 
> > User Defined Attributes (UDA) are compile time expressions that can be attached to a declaration. These attributes can then be queried, extracted, and manipulated at compile time. There is no runtime component to them.
> > 
> > Grammatically, a UDA is a StorageClass:
> > 
> >     StorageClass:
> > 	UserDefinedAttribute
> > 
> >     UserDefinedAttribute:
> >         [ ArgumentList ]
> > 
> > And looks like:
> > 
> >     [ 3 ] int a;
> >     [ "string", 7 ]: int b;
> > 
> > If there are multiple UDAs in scope for a declaration, they are concatenated:
> > 
> >     [ 1 ] {
> >        [ 2 ] int a;        // has UDA's [1,2]
> >        [ "string" ] int b; // has UDA's [1,"string"]
> >     }
> > 
> > UDA's can be extracted into an expression tuple using __traits:
> > 
> >     [ 'c' ] string s;
> >     pragma(msg, __traits(getAttributes, s));
> > 
> > prints:
> > 
> >     tuple('c')
> > 
> > If there are no user defined attributes for the symbol, an empty tuple is returned. The expression tuple can be turned into a manipulatable tuple:
> > 
> >   template Tuple(T...) {
> >     alias T Tuple;
> >   }
> > 
> >   enum EEE = 7;
> >   ["hello"] struct SSS { }
> >   [3] { [4][EEE][SSS] int foo; }
> > 
> >   alias Tuple!(__traits(getAttributes, foo)) TP;
> > 
> >   pragma(msg, TP);
> >   pragma(msg, TP[2]);
> > 
> > prints:
> > 
> >   tuple(3,4,7,(SSS))
> >   7
> > 
> > and of course the tuple types can be used to declare things:
> > 
> >   TP[3] a;    // a is declared as an SSS
> > 
> > The attribute of the type name is not the same as the attribute of the variable:
> > 
> >   pragma(msg, __traits(getAttributes, typeof(a));
> > 
> > prints:
> > 
> >     tuple("hello")
> > 
> > Of course, the real value of UDA's is to be able to create user defined types with
> > specific values. Having attribute values of basic types does not scale.
> > The attribute tuples can be manipulated like any other tuple, and
> > can be passed as the argument list to a template.
> > 
> > Whether the attributes are values or types is up to the user, and whether later
> > attributes accumulate or override earlier ones is also up to how the
> > user interprets them.
> 
> I wonder what are the benefits over a library solution. Something like
> 
> struct UserDefinedAttribute(Args...)
> {
> 	alias Args[0 .. $ -1] attributes;
> }
> 
> unittest
> {
> 	UserDefinedAttribute!("my attr", int) a;
> 	UserDefinedAttribute!("my attr", 4, int) b;
> 
> 	import std.stdio;
> 	writeln(typeof(a).attributes.stringof);
> 	writeln(typeof(b).attributes.stringof);
> }
> 
> which admittedly has less syntactical appeal (and probably other
> problems) but can maybe improved. But to which point.
> What do you gain by adding it to the core language?

No need to reply.
Since you said already
"Yes, you can attach them to other symbols, including user defined types."
That wasn't obvious from the examples. That's why it looked initially
limited to me.

Jens
November 06, 2012
Tooo much fun! Argh, _must_ _stop_ _playing_ and actually work ;(

["int a;"]
class A
{
}

class B : A
{
  mixin(__traits(getAttributes, typeof(super))[0]);
}

can't wait to see all the creative uses this will enable!

November 06, 2012
On Tuesday, 6 November 2012 at 10:10:37 UTC, Tove wrote:
> Tooo much fun! Argh, _must_ _stop_ _playing_ and actually work ;(
>
> ["int a;"]
> class A
> {
> }
>
> class B : A
> {
>   mixin(__traits(getAttributes, typeof(super))[0]);
> }
>
> can't wait to see all the creative uses this will enable!

Or obfuscation :)
November 06, 2012
On Tuesday, 6 November 2012 at 09:24:58 UTC, Jens Mueller wrote:
> which admittedly has less syntactical appeal (and probably other
> problems) but can maybe improved. But to which point.
> What do you gain by adding it to the core language?
>
> Jens

In the initial discussions, I was hoping that the symbol itself would be made available in some fashion. For example, enabling something like this:

template Test(alias sym)
{
    pragma(msg, "test attribute was attached to:");
    pragma(msg, sym);
    enum Test = true;
}

[Test] int a;

However, I can imagine that it's always possible to work around this by doing the actual work later when both the symbol and attribute are available.

November 06, 2012
On Tuesday, 6 November 2012 at 09:07:34 UTC, Walter Bright wrote:
> Is that really a problem?

I don't strictly need it for any of my projects, where I'm already using lazy initialization for these cases. I guess time will tell if it's significant for other applications.