April 06, 2012
On 4/6/2012 2:50 AM, Ary Manzana wrote:
> The syntax in Java for declaring an attribute:
>
> public @interface Foo {
> String xxx;
> int yyy;
> }
>
> In D maybe @interface could be used to (in order to avoid introducing another
> keyword... or maybe use @attribute instead):
>
> @attribute Foo {
> string xxx;
> int yyy;
> }

I don't see the need for creating a new kind of symbol.


> 2. You use them by using their names. What you are proposing if for attribute
> foo to be @attr(foo). But in Java it's @foo.
>
> So in Java you would use that attribute like this:
>
> @Foo(xxx = "hello", yyy = 1);
> void func() {}
>
> Then you can get the "Foo" attribute in func, and ask for it's xxx and yyy.

This is a runtime system.


> Now, your proposal is much simpler and it will become inconvenient in some
> cases. For example suppose you want to provide attributes for serialization (I
> guess the classic example). With your proposal it would be:
>
> /// This is actually an attribute. Use this together with serialized_name.
> enum serialize = 1;
> enum serialized_name = 2;
>
> @attr(serialize = true, serialized_name = "Foo")
> int x;

No, it would be:

   enum serialize = true;
   enum serialize_name = "Foo";
   @attr(serialize, serialized_name) int x;

There would be no initialization in the @attr syntax.

> Now, with the way things are done in Java and C#:
>
> /// Marks a field to be serialized.
> @attribute serialize {
> /// The name to be used.
> /// If not specified, the name of the member will be used instead.
> string name;
> }
>
> @serialize(name = "Foo")
> int x;
>
> You can see the syntax is much cleaner. The attribute declaration also serves as
> documentation and to group attributes related to the serialization process.

I don't see that it is cleaner - there's no particular reason why a new symbol type needs to be introduced.

>
> Now, to implement this is not very much difficult than what you proposed.
>
> 1. Introduce the syntax to define attributes. Piece of cake, since it's much
> more or less the syntax of a struct, but functions or nested types are not
> allowed. Parse them into an AttributeDecl or something like that.
> 2. When the compiler finds @attr(field = value) it uses normal lookup rules to
> find "attr". Then it checks it's an attributes. Then all fields are check in
> turn to see if their type match. You can probably put there anything that's
> compile-time evaluatable, though usually primitive types and strings are enough.
> If a field is not specified, it's type.init will be used.
> 3. The syntax for querying is almost the same as you proposed:
>
> __traits(hasAttribute, x, serializable) // true
> __traits(getAttribtue, x, serializable, name) // "Foo"
>
> 4. Declare the core attributes in object.di or similar: @safe, @nothrow, etc.
> You can also document them.
> 5. Probably deprecate __traits(isSafe) and so on, since hasAttribute can be used
> for that.

@safe, @nothrow, etc., require a lot of semantic support in the compiler. They cannot pretend to be user defined attributes.
April 06, 2012
On 4/6/2012 1:15 AM, Alex Rønne Petersen wrote:
> On 06-04-2012 09:54, Walter Bright wrote:
>> On 4/6/2012 12:49 AM, Alex Rønne Petersen wrote:
>>> What about type declarations? I think those ought to be supported too.
>>> E.g. it
>>> makes sense to mark an entire type as @attr(serializable) (or the
>>> inverse).
>>
>>
>> That would make it a "type constructor", not a storage class, which we
>> talked about earlier in the thread. I refer you to that discussion.
>
> To be clear, I don't want annotations on a type declaration to actually affect
> the type. Annotations are just that: Annotations. Nothing else.

What would an annotation on a type mean if it does not affect the type? Would it just be baggage carried along? If so, how would that affect types inside of a template, where the type was passed as a type constructor? How would type inference work? Would an alias carry or drop the baggage? How would overloading work? How would covariance work? It's a maze of complex decisions.


> Does a design like that still give rise to the semantic issues you mentioned (it
> isn't clear what those are)?

Yes.

(I know Java does this. But Java has a trivial type system compared with D. It doesn't even have type aliases. It doesn't have type constructors. Etc.)
April 06, 2012
On 4/6/2012 2:18 AM, Ary Manzana wrote:
> On 4/6/12 3:54 PM, Walter Bright wrote:
>> On 4/6/2012 12:49 AM, Alex Rønne Petersen wrote:
>>> What about type declarations? I think those ought to be supported too.
>>> E.g. it
>>> makes sense to mark an entire type as @attr(serializable) (or the
>>> inverse).
>>
>>
>> That would make it a "type constructor", not a storage class, which we
>> talked about earlier in the thread. I refer you to that discussion.
>
> What's the difference between "type constructor" and "storage class" beside the
> name?

static const(int)* foo;

static is a storage class. const is a type constructor. There is no type 'static'.
April 06, 2012
On 6 April 2012 09:47, Walter Bright <newshound2@digitalmars.com> wrote:

> On 4/5/2012 5:00 AM, Manu wrote:
> > C# and Java both have attributes, following these established design
> patterns, I
> > don't think there should be any mystery over how they should be
> implemented.
>
> I was thinking of something along the lines of what has been proposed here earlier:
>
>  @attr(identifier = expression)
>
> as a storage class, like:
>
>  @attr(foo = bar + 1) int x;
>
> and then:
>
>  __traits(hasAttribute, x, foo)
>
> would return true, and:
>
>  __traits(getAttribute, x, foo)
>
> would return the expression (bar+1). The expression would be compile-time only, evaluated at the point of declaration.
>
> The implementation is simple enough, just attach to each symbol an array of (identifier,expression) pairs.
>
> You could also omit the expression, and just have:
>
>  @attr(bar) int y;
>

I think this is unnecessarily verbose, although certainly workable as a minimum. Although there is one issue that 'foo' can't store compound data. I'd like to see attributes defined as a special sort of struct, and support constructors, this way you can associate rich data, and you can perform complex expressions (ctfe) within the constructor to set defaults, or calculate other details about the attribute if not specified.

@replay int x; // this is all that is needed if the attribute associates no variable information

// or something associating consideraly more rich information
@editor("X coordinate", EditType.Slider, Colour.Red, "Verbose description
about the thing") // this is still a lot easier to read to me...
int x;


April 06, 2012
On 6 April 2012 10:48, Walter Bright <newshound2@digitalmars.com> wrote:

> On 4/6/2012 12:35 AM, Alex Rønne Petersen wrote:
>
>> It actually can be a problem. In .NET land, there are many attributes
>> across
>> many projects (and even in the framework itself) with the same names. It
>> turns
>> out that regular namespace lookup rules alleviate this problem.
>>
>
>
> Perhaps a better scheme is:
>
>   enum foo = 3;
>
>   ...
>
>   @attr(foo) int x;
>
> That way, foo will follow all the usual rules.
>

What about:

struct editor
{
  this(string name, EditType, Colour = Colour.Default, string description =
null)
  {
    //...
  }

  blah blah blah
}

@attr(editor("thing",...blah...))

I don't see the advantage over:
@editor(...)

?


April 06, 2012
On 4/6/2012 2:54 AM, Timon Gehr wrote:
> Should add additional information to the type Foo. I don't see any issues with
> it, and not supporting it would be very strange.

How would:

   @attr(foo) int x;
   int y;

work? Are x and y the same type or not? Now, consider:

   auto c = b ? x : y;

What type does c have? int or @attr(foo)int ? And that's really just the beginning. How about:

  struct S(T) {
    T t;
  }

Instantiate it with S!int and S!(@attr(foo)int). Are those the same instantiation, or different? If the same, does S.t have the attribute or not?

And, whatever you choose for the semantics, what is the sensible, intuitive rule for it?
April 06, 2012
On 6 April 2012 12:41, Johannes Pfau <nospam@example.com> wrote:

> Am Fri, 06 Apr 2012 00:48:34 -0700
> schrieb Walter Bright <newshound2@digitalmars.com>:
>
> > On 4/6/2012 12:35 AM, Alex Rønne Petersen wrote:
> > > It actually can be a problem. In .NET land, there are many attributes across many projects (and even in the framework itself) with the same names. It turns out that regular namespace lookup rules alleviate this problem.
> >
> >
> > Perhaps a better scheme is:
> >
> >     enum foo = 3;
> >
> >     ...
> >
> >     @attr(foo) int x;
> >
> > That way, foo will follow all the usual rules.
> >
>
> The last time custom attributes where discussed, a C# like model was proposed. Is there a good reason why we should deviate from the C# implementation?
>
> C#:
> http://msdn.microsoft.com/en-US/library/48zeb25s(v=vs.80).aspx
> http://msdn.microsoft.com/en-US/library/sw480ze8(v=vs.100).aspx
> http://msdn.microsoft.com/en-US/library/z919e8tw(v=vs.80).aspx
>
> (C# attributes can be applied to almost everything, therefore sometimes
>  "disambiguating targets" is necessary. Probably not needed in D):
> http://msdn.microsoft.com/en-US/library/b3787ac0(v=vs.80).aspx
>
> Syntax in D would be different of course, but I see absolutely no need for the redundant (and ugly) @attr.
>
> Declaring a custom attribute:
> ---------
> module std.something;
>
> struct Author
> {
>    string name;
>    public this(string name)
>    {
>        this.name = name;
>    }
> }
> ---------
>
> Using it:
> ---------
> import std.something; //Usual namespace lookup rules apply to attributes
>
> /*
>  * @Author(param) calls the constructor of the Author struct and
>  * attaches the struct instance to test. Probably @Author (without
>  * parenthesis) coud be made to mean std.something.Author.init
>  */
> @Author("Johannes Pfau") int test;
> ---------
>
> Attaching attributes multiple times as in C# should be possible.
>
> Using reflection to get that attribute:
> ---------
> if(__traits(hasAttribute, test, std.something.Author))
> {
>    Author[] authors = __traits(getAttribute, test,
>        std.something.Author);
> }
> ---------
>
> An array is used here to support attaching the same attribute multiple times. Of course "auto authors = ..." should be usable here too.
>

+1


April 06, 2012
On 4/6/12 6:12 PM, Walter Bright wrote:
> On 4/6/2012 2:50 AM, Ary Manzana wrote:
>> The syntax in Java for declaring an attribute:
>>
>> public @interface Foo {
>> String xxx;
>> int yyy;
>> }
>>
>> In D maybe @interface could be used to (in order to avoid introducing
>> another
>> keyword... or maybe use @attribute instead):
>>
>> @attribute Foo {
>> string xxx;
>> int yyy;
>> }
>
> I don't see the need for creating a new kind of symbol.
>
>
>> 2. You use them by using their names. What you are proposing if for
>> attribute
>> foo to be @attr(foo). But in Java it's @foo.
>>
>> So in Java you would use that attribute like this:
>>
>> @Foo(xxx = "hello", yyy = 1);
>> void func() {}
>>
>> Then you can get the "Foo" attribute in func, and ask for it's xxx and
>> yyy.
>
> This is a runtime system.

Yes, but I'm thinking about a compile-time system (as I showed in the example usages below).

>
>
>> Now, your proposal is much simpler and it will become inconvenient in
>> some
>> cases. For example suppose you want to provide attributes for
>> serialization (I
>> guess the classic example). With your proposal it would be:
>>
>> /// This is actually an attribute. Use this together with
>> serialized_name.
>> enum serialize = 1;
>> enum serialized_name = 2;
>>
>> @attr(serialize = true, serialized_name = "Foo")
>> int x;
>
> No, it would be:
>
> enum serialize = true;
> enum serialize_name = "Foo";
> @attr(serialize, serialized_name) int x;
>
> There would be no initialization in the @attr syntax.

Hmmm... I didn't get that quite well. You are using the symbol's name as the attribute name? In my example I missed splitting it into two files:

my_attributes.d:

@attribute serialize { }

my_usage_of_it.d:

import my_attributes;

@serialize(...)


>
>> Now, with the way things are done in Java and C#:
>>
>> /// Marks a field to be serialized.
>> @attribute serialize {
>> /// The name to be used.
>> /// If not specified, the name of the member will be used instead.
>> string name;
>> }
>>
>> @serialize(name = "Foo")
>> int x;
>>
>> You can see the syntax is much cleaner. The attribute declaration also
>> serves as
>> documentation and to group attributes related to the serialization
>> process.
>
> I don't see that it is cleaner - there's no particular reason why a new
> symbol type needs to be introduced.
>
>>
>> Now, to implement this is not very much difficult than what you proposed.
>>
>> 1. Introduce the syntax to define attributes. Piece of cake, since
>> it's much
>> more or less the syntax of a struct, but functions or nested types are
>> not
>> allowed. Parse them into an AttributeDecl or something like that.
>> 2. When the compiler finds @attr(field = value) it uses normal lookup
>> rules to
>> find "attr". Then it checks it's an attributes. Then all fields are
>> check in
>> turn to see if their type match. You can probably put there anything
>> that's
>> compile-time evaluatable, though usually primitive types and strings
>> are enough.
>> If a field is not specified, it's type.init will be used.
>> 3. The syntax for querying is almost the same as you proposed:
>>
>> __traits(hasAttribute, x, serializable) // true
>> __traits(getAttribtue, x, serializable, name) // "Foo"
>>
>> 4. Declare the core attributes in object.di or similar: @safe,
>> @nothrow, etc.
>> You can also document them.
>> 5. Probably deprecate __traits(isSafe) and so on, since hasAttribute
>> can be used
>> for that.
>
> @safe, @nothrow, etc., require a lot of semantic support in the
> compiler. They cannot pretend to be user defined attributes.

Yes they can. That's how it is done in C# and Java. In fact, IUnknown is pretending to be an interface and has semantic support in the compiler.
April 06, 2012
On 4/6/2012 3:23 AM, Manu wrote:
> What about:
>
> struct editor
> {
>    this(string name, EditType, Colour = Colour.Default, string description = null)
>    {
>      //...
>    }
>
>    blah blah blah
> }
>
> @attr(editor("thing",...blah...))

Are you really changing the arguments for every declaration? Or is it one set that is repeated everywhere?


> I don't see the advantage over:
> @editor(...)
>
> ?

Name collisions with other @ attributes.

April 06, 2012
On 04/06/2012 12:12 PM, Walter Bright wrote:
> On 4/6/2012 2:50 AM, Ary Manzana wrote:
>> The syntax in Java for declaring an attribute:
>>
>> public @interface Foo {
>> String xxx;
>> int yyy;
>> }
>>
>> In D maybe @interface could be used to (in order to avoid introducing
>> another
>> keyword... or maybe use @attribute instead):
>>
>> @attribute Foo {
>> string xxx;
>> int yyy;
>> }
>
> I don't see the need for creating a new kind of symbol.
>

It would behave like a struct anyway. The issue is whether any struct should be allowed to be used as an attribute, or whether a runtime instance of an attribute can be created.

Syntax could just as well be this:

@attribute struct Foo {
    // ...
}

>
>> 2. You use them by using their names. What you are proposing if for
>> attribute
>> foo to be @attr(foo). But in Java it's @foo.
>>
>> So in Java you would use that attribute like this:
>>
>> @Foo(xxx = "hello", yyy = 1);
>> void func() {}
>>
>> Then you can get the "Foo" attribute in func, and ask for it's xxx and
>> yyy.
>
> This is a runtime system.
>

In Java, yes. But the general scheme works well even at compile time.

It is possible to evaluate struct constructors at compile time.

enum Foo attr = __traits(getAttribute, func, Foo);

>
>> Now, your proposal is much simpler and it will become inconvenient in
>> some
>> cases. For example suppose you want to provide attributes for
>> serialization (I
>> guess the classic example). With your proposal it would be:
>>
>> /// This is actually an attribute. Use this together with
>> serialized_name.
>> enum serialize = 1;
>> enum serialized_name = 2;
>>
>> @attr(serialize = true, serialized_name = "Foo")
>> int x;
>
> No, it would be:
>
>     enum serialize = true;
>     enum serialize_name = "Foo";
>     @attr(serialize, serialized_name) int x;
>

I don't see how that would work. Do you propose to define attributes based on the mapping of the name of random manifest constants to their value? That would be a kludgy hack.

> There would be no initialization in the @attr syntax.
>
>> Now, with the way things are done in Java and C#:
>>
>> /// Marks a field to be serialized.
>> @attribute serialize {
>> /// The name to be used.
>> /// If not specified, the name of the member will be used instead.
>> string name;
>> }
>>
>> @serialize(name = "Foo")
>> int x;
>>
>> You can see the syntax is much cleaner. The attribute declaration also
>> serves as
>> documentation and to group attributes related to the serialization
>> process.
>
> I don't see that it is cleaner

I think it is cleaner on a conceptual level, even if the syntax was

@attr(serialize("Foo"))

> - there's no particular reason why a new
> symbol type needs to be introduced.
>

There is not strictly a need for a _new_ symbol type, but attribute lookup should definitely be symbol based and not string/value based. Why would there be any benefit in bypassing the module system for user defined attributes?