April 06, 2012
On Fri, 06 Apr 2012 13:33:33 -0400, Tove <tove@fransson.se> wrote:
> I think this proposal pretty much covers what I would expect from 'custom attributes'... but what about adding a D twist, getting "what we annotate" as a template parameter so that one among other things can make use of Template Constraints?

Interesting, so something like:

@attribute string defaultName(T)() if(is(typeof(T.init.name))) { return T.init.name;}

Not sure how much this gives us, but it definitely feels doable.

-Steve
April 06, 2012
On 04/06/2012 07:04 AM, Manu wrote:
> I think Johannes proposal already nails it. What benefits would be
> merged from this proposal? How would they influence the design?
>
>     On 04/06/2012 11:41 AM, Johannes Pfau wrote:
>
>         Declaring a custom attribute:
>         ---------
>         module std.something;
>
>         struct Author
>         {
>             string name;
>             public this(string name)
>             {
>         this.name <http://this.name> = name;
>             }
>         }
>         ---------

Why not being more flexible .. Likewise

struct Annotation  //Throw in all your annotations
{

	Variant[] [string] map;

	Variant[] opDispatch(string key)()
	{
       		return map[key];
    	}	
	
	// Single value
	Variant[] opDispatch(string key, T) (T t )
	if ( !isArray!T && !isTuple!T )
	{
		index ~= key;
		map[key] ~= to!Variant(t);
		return map[key];
	}	
	....Array, Tuple	
}
well.. I am not sure about CTFE
April 06, 2012
On 04/06/2012 10:51 AM, bls wrote:
> On 04/06/2012 07:04 AM, Manu wrote:
>> I think Johannes proposal already nails it. What benefits would be
>> merged from this proposal? How would they influence the design?
>>
>> On 04/06/2012 11:41 AM, Johannes Pfau wrote:
>>
>> Declaring a custom attribute:
>> ---------
>> module std.something;
>>
>> struct Author
>> {
>> string name;
>> public this(string name)
>> {
>> this.name <http://this.name> = name;
>> }
>> }
>> ---------
>

Oh the joy of copy and paste ..

struct Annotation  //Throw in all your annotations
{

    Variant[] [string] map;

    Variant[] opDispatch(string key)()
    {
               return map[key];
    }

    // Single value
    void opDispatch(string key, T) (T t )
    if ( !isArray!T && !isTuple!T )
    {
       	map[key] ~= to!Variant(t);
	return;
     }
    // Allow ....Array and Tuple too
}
April 06, 2012
On Friday, 6 April 2012 at 17:44:25 UTC, Steven Schveighoffer wrote:
> On Fri, 06 Apr 2012 13:33:33 -0400, Tove <tove@fransson.se> wrote:
>> I think this proposal pretty much covers what I would expect from 'custom attributes'... but what about adding a D twist, getting "what we annotate" as a template parameter so that one among other things can make use of Template Constraints?
>
> Interesting, so something like:
>
> @attribute string defaultName(T)() if(is(typeof(T.init.name))) { return T.init.name;}
>
> Not sure how much this gives us, but it definitely feels doable.
>
> -Steve

yes, exactly... well, once library designers start getting creative, one of the immediate benefits would be, easy to understand error-messages.
April 06, 2012
Am 06.04.2012 17:17, schrieb Adam D. Ruppe:
> On Friday, 6 April 2012 at 15:07:04 UTC, Manu wrote:
>> But maybe the function approach has an
>> effect on the simplicity of the expression for a simple attribute,
>> like a single bool?
>
> Meh, it is pretty similar:
>
> struct Serializable { bool yes; }
> bool Serializable(bool yes) { return yes; }
>

There's one difference I think.
struct approach:
struct Area { int x, y; }
Area sqare(int a) { return Area(x, y); }
//foo and bar are attributed the same
@Area(5, 5) int foo();
@square(5) int bar();

whereas with the function approach:
@area(5, 5) int foo();
@square(5) int bar();
foo and bar have different attributes.

The problem is you can't define forwarding functions because the symbol is the attribute type. This seems to be a major problem to me.

Mafi
April 06, 2012
On Fri, 06 Apr 2012 14:23:45 -0400, Mafi <mafi@example.org> wrote:

> Am 06.04.2012 17:17, schrieb Adam D. Ruppe:
>> On Friday, 6 April 2012 at 15:07:04 UTC, Manu wrote:
>>> But maybe the function approach has an
>>> effect on the simplicity of the expression for a simple attribute,
>>> like a single bool?
>>
>> Meh, it is pretty similar:
>>
>> struct Serializable { bool yes; }
>> bool Serializable(bool yes) { return yes; }
>>
>
> There's one difference I think.
> struct approach:
> struct Area { int x, y; }
> Area sqare(int a) { return Area(x, y); }
> //foo and bar are attributed the same
> @Area(5, 5) int foo();
> @square(5) int bar();
>
> whereas with the function approach:
> @area(5, 5) int foo();
> @square(5) int bar();
> foo and bar have different attributes.
>
> The problem is you can't define forwarding functions because the symbol is the attribute type. This seems to be a major problem to me.

I acknowledge this limitation.  But we can also overload functions:

@attribute Area area(int w, int h) { return Area(w, h);}
@attribute Area area(int w) { return Area(w, w);}

Granted, area is not as obvious as square (it's actually a bad name, it should be something like dimensions), but being able to have more than one attribute of the same type I think is essential.

Also, if I see:

@square(5) int foo();

How do I know that I have to use __traits(getAttribute, foo, Area)?

Another possibility:

@attribute Area area(int w, int h) { return Area(w, h);}
@attribute Area area(Area a) { return a;}

Area square(int a) { return Area(a, a);}

@area(5, 5) int foo();
@area(square(5)) int bar();

-Steve
April 06, 2012
Steven Schveighoffer wrote:
> On Fri, 06 Apr 2012 10:56:19 -0400, Piotr Szturmaj
> <bncrbme@jadamspam.pl> wrote:
>
>> Steven Schveighoffer wrote:
>>> You can store a struct, just return it from an attribute function.
>>>
>>> e.g.:
>>>
>>> @attribute Author author(string name) { return Author(name);}
>>
>> Compare it to:
>>
>> struct Author { string name; }
>>
>> @Author("John Doe") int x;
>
> so now I must define a type for every attribute? I'd rather just define
> a function.

So declaration is always needed, no matter what attribute is.

> What if I have 20 string attributes, I must define a new attribute type
> for each one? This seems like unneeded bloat.

I don't see advantage of functions here, twenty of them is also a bloat. Different types give you different _names_ for different purposes. Those _names_ are crucial to support the attributes.

How do you get list of all attributes with your function based proposal? You can get a string attribute but you don't know which function generated it. You don't know if it was serializable, author or whatever.

> BTW, if I wasn't trying to demonstrate that you could store structs, I
> would have written:
>
> @attrubte string author(string name) { return name;}
>
> and save the extra bloat associated with declaring another type. Maybe
> we could even get this someday:

As above, declaring another function is also a bloat.

> @attribute author(string name) => name;
>
> I just don't see the need to declare a type that can wrap a string.
>
> You could even add this rule:
>
> if @attribute is placed on a struct, its constructor becomes an
> @attribute qualified function with the name of the struct as the
> attribute name.

Consider struct constructors as equivalent of functions proposed by you. Here you declare a type, there you declare a function. They're very similar, besides that struct type _describes_ the attribute. A function on the other side just returns a value, which doesn't have any name attached to it.

>>> Why should we be restricted to only structs? Or any type for that
>>> matter?
>>
>> When using __traits(getAttributes, ...) you ask for conrete (struct)
>> type and you get it. In case of function you ask for serializable but
>> you get a bool.
>
> It's an example. you can choose any type you want! I actually just want
> the name of the author, I don't care whether that's a struct, or a string.

Yes, but my point is that you get a bool and you don't know which of the functions returned it, as many of them can return bool.

>>> The benefit to using CTFE functions is that the compiler already knows
>>> how to deal with them at compile-time. i.e. less work to make the
>>> compiler implement this.
>>
>> Compiler can easily deal with structs too:
>
> I concede this is probably a non-issue.
>
>>> I also firmly believe that determining what is allowed as attributes
>>> should be opt-in. Just allowing any struct/class/function/etc. would
>>> lead to bizarre declarations.
>>
>> C# has requirement that attributes are classes that derive from base
>> Attribute class. But without that limitation you can do things like:
>>
>> @Uuid("...")
>> interface MyIntf { }
>>
>> without explicitly declaring Uuid as attribute. However, I don't see
>> any usage for primitive types:
>>
>> @5
>> @"s"
>> @false
>
> I don't understand what you are saying here.

"Just allowing any struct/class/function/etc. would
lead to bizarre declarations."

Allowing _any_ type would indeed lead to bizarre declarations, but I think that allowing any struct or class may be useful.

Alternatively structs or classes may require some additional member. This will allow only selected types to work as attributes.

>> I think that allowing values of structs, classes and _eventually_
>> enums should be enough.
>
> Any CTFE computed value should suffice.

I think that list of attributes should be a list of user defined types. You can always write a function to construct them, but anyway you get named user defined type (like struct). Named type easily disambiguates between different attributes without resorting to name-value solutions.

This is how it's done in C# by the way.
April 06, 2012
Steven Schveighoffer wrote:
> On Fri, 06 Apr 2012 13:33:33 -0400, Tove <tove@fransson.se> wrote:
>> I think this proposal pretty much covers what I would expect from
>> 'custom attributes'... but what about adding a D twist, getting "what
>> we annotate" as a template parameter so that one among other things
>> can make use of Template Constraints?
>
> Interesting, so something like:
>
> @attribute string defaultName(T)() if(is(typeof(T.init.name))) { return
> T.init.name;}
>
> Not sure how much this gives us, but it definitely feels doable.

See also: http://forum.dlang.org/post/jlmtcv$v09$1@digitalmars.com
April 06, 2012
On 4/6/12 6:23 AM, Steven Schveighoffer wrote:
> 6. The metadata is stored in a key-value pair, with the key being the
> symbol of the @attribute function, and the value being the result of the
> CTFE function.

There may be a good reason why it's not supported, but in Java I've often wished I could repeat an annotation with different values. 'Twould be fantastic if D allowed this.
April 06, 2012
On Fri, 06 Apr 2012 15:03:39 -0400, Piotr Szturmaj <bncrbme@jadamspam.pl> wrote:

> Steven Schveighoffer wrote:

>> What if I have 20 string attributes, I must define a new attribute type
>> for each one? This seems like unneeded bloat.
>
> I don't see advantage of functions here, twenty of them is also a bloat. Different types give you different _names_ for different purposes. Those _names_ are crucial to support the attributes.

Unused function do not make it into the EXE.

> How do you get list of all attributes with your function based proposal? You can get a string attribute but you don't know which function generated it. You don't know if it was serializable, author or whatever.

foreach(name, value; __traits(getAttributes, symbol)) {...}

hereby added to the proposal.

>
>> BTW, if I wasn't trying to demonstrate that you could store structs, I
>> would have written:
>>
>> @attrubte string author(string name) { return name;}
>>
>> and save the extra bloat associated with declaring another type. Maybe
>> we could even get this someday:
>
> As above, declaring another function is also a bloat.

No, it doesn't generate more typeinfo that must go into the EXE.  When the EXE is built, all associated bloat should disappear, it's only needed during compilation.

>> @attribute author(string name) => name;
>>
>> I just don't see the need to declare a type that can wrap a string.
>>
>> You could even add this rule:
>>
>> if @attribute is placed on a struct, its constructor becomes an
>> @attribute qualified function with the name of the struct as the
>> attribute name.
>
> Consider struct constructors as equivalent of functions proposed by you. Here you declare a type, there you declare a function. They're very similar, besides that struct type _describes_ the attribute. A function on the other side just returns a value, which doesn't have any name attached to it.

The name is the function.  You seem to be arguing the equivalent of:

"int x is useless.  It should really just be int.  If you need another integer field, make a new type that's just like int, how hard is that?"

Yeah, I know it's a strawman, but this is seriously how it sounds to me ;)

>> It's an example. you can choose any type you want! I actually just want
>> the name of the author, I don't care whether that's a struct, or a string.
>
> Yes, but my point is that you get a bool and you don't know which of the functions returned it, as many of them can return bool.

I think you are missing how the metadata is stored as key-value pairs, with the key being the name of the function that was used.

>> Any CTFE computed value should suffice.
>
> I think that list of attributes should be a list of user defined types. You can always write a function to construct them, but anyway you get named user defined type (like struct). Named type easily disambiguates between different attributes without resorting to name-value solutions.

Again, see point above about not naming variables.

> This is how it's done in C# by the way.

Yes I know.  I don't think we need to limit ourselves this way, C# does not have the compile-time power that D does.

-Steve