Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
March 16, 2012 Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
On the ride over here today, I had a thought that I think neatly solves the user defined attribute question. enum Serializable { yes, no } @note(Serializable.yes) int a; We introduce two new things to the language: 1) the @note(expression) attribute. You can put as many "notes" on a declaration as you want by simply listing them. The expression inside is appended to a list on the declaration. This would be valid on variables, functions, function parameters, struct members, etc. 2) __traits(getNotes, decl). This returns a tuple of all the note expressions. foreach(i, exp; __traits(getNotes, a)) { static assert(is(typeof(exp) == Serializable); static assert(exp == Serializable.yes); } This simple extension to the language enables libraries, using existing traits to navigate symbols, to get additional information about things and implement it however. The lack of user defined attributes is D's biggest missed opportunity right now, and I think this simple proposal will plug that hole. Previous user-defined attribute proposals have had counter arguments like these: 1) how do you define what is and is not a valid attribute? Here, the answer is pretty simple: you can put whatever notes you want on the thing. Whether the library uses it or not is up to it. It has to be a valid type - so the compiler will catch typos and whatnot - but it doesn't have to be used unless you ask for it. 2) OK, how do you namespace things so different libraries don't get different results? That's where the beauty of the expression type comes in: D already has namespaces. foo.Serializable != bar.Serializable, so there's no conflict. We simply declare types. The enum X {yes, no} pattern is already common in Phobos, and is also the best way to express a bool condition here. (Technically, @note(true) would compile, but it'd be much less useful than declaring a name for the note too, with an enum.) Name conflicts is a problem already solved by the language. 3) Can we have shared attributes, with declared names so people know what to use in their own libraries? Yes, we could add some to std.traits or make a new std.notes that declare some common items as enums. Though, I think having the types declared in the modules that use them is generally more useful. But, again, the library problem is already solved, and we're just using that! I think this is a winner... and I think I can do it in the compiler in less time than it will take to convince git to give me a clean pull request. Am I missing anything? |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe Attachments:
| On 16 March 2012 15:35, Adam D. Ruppe <destructionator@gmail.com> wrote: > @note(Serializable.yes) int a; > Surely the term you're looking for here is @annotate(...) ? This simple extension to the language enables libraries, > using existing traits to navigate symbols, to get additional information about things and implement it however. > > The lack of user defined attributes is D's biggest missed opportunity right now, and I think this simple proposal will plug that hole. > I agree it's a very big hole against popular competing languages (Java, C#). Previous user-defined attribute proposals have had counter > arguments like these: > > 1) how do you define what is and is not a valid attribute? > > Here, the answer is pretty simple: you can put whatever notes > you want on the thing. Whether the library uses it or not > is up to it. > What if you want to annotate with a variable? Each instance may need to hold some attribute state. This is extremely common, particularly in serialisation systems which typically perform lazy updates, and need some additional state for that. > 2) OK, how do you namespace things so different libraries don't get different results? > Surely this is just as easy: @modulename.attribute int myThing; > Am I missing anything? > I think it only solves half the problem. There are certainly cases where you just want to annotate with some sort of tag, so you can know to do something special in this thing's case, but there are also times where you want to associate some variable data with each instance. Perhaps that's the key distinction between 'annotation' and a 'custom attributes' .. an annotation this way is a shared compile time constant, associating with its type info, as you suggest. A custom attribute is an instance-specific association vontaining variable data. I'd suggest that @annotate(someEnum) could work effectively just as you suggest, but it doesn't fill the requirement for custom attributes, which could be implemented separately, something like: attribute myAttribute { this(int something); bool bNeedsAttention; property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; } } @myAttribute(10) int thing; thing.refresh = true; |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 16 March 2012 at 14:11:35 UTC, Manu wrote: > Surely the term you're looking for here is @annotate(...) ? Meh, I don't care that much about names. I went with "note" anticipating people would complain about @add_user_defined_attribute() being too long :) > What if you want to annotate with a variable? That *might* work because a variable is an expression too. I'm not sure though. Will probably have to implement to know for sure. Of course, a constant can be represented as a struct for name and value: struct Description { string s; } @note(Description("yada yada yada")) int a; > Surely this is just as easy: @modulename.attribute int myThing; Yeah, I think so. I remember this being a counterpoint last time we talked about it, but I don't recall the specific argument made. > Perhaps that's the key distinction between 'annotation' and a 'custom attributes' .. an annotation this way is a shared compile time constant, associating with its type info, as you suggest. A custom attribute is an instance-specific association vontaining variable data. Yeah, "annotation" might be the better word. That's what I want here, but too late to change the subject name now. > attribute myAttribute I think this could be done as a struct, but I haven't used this kind of attribute at all so not sure... But what I'm thinking is: struct myAttribute(Type) { Type value; alias value this; bool bNeedsAttention; @property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; } } myAttribute!int thing; thing.refresh = true; and thing can be substituted for an int anywhere else. |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | Another argument against would be "can we do this in the language today?" And the answer is "sort of, but not really": void a(@note(...) int arg) {} You could perhaps do something like: void a(int arg) {} mixin("enum " ~ a.mangleof ~ "_arg = " ~ ...); and then get the listing by looking at allMembers and comparing the name... but, it gets messy fast - the attribute is somewhat far from the declaration, so I betcha it will get out of sync. Getting the attribute on param 0 too would consist of using stringof tricks to get the name, then mixing it in to get that variable. This isn't a proper tuple either - more care would be needed to handle multiple notes. So, you could make it work, but it is hideous, fragile, and probably less useful even if you look past that. I've seen some nice implementations of struct level annotations, using mixin templates, but the killer feature for me, personally, is putting it on function parameters as well. I think the language addition (which breaks nothing existing today; it is purely additive) is necessary to get something generally useful, not just useful in a few cases. |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe Attachments:
| On 16 March 2012 16:51, Adam D. Ruppe <destructionator@gmail.com> wrote:
> On Friday, 16 March 2012 at 14:11:35 UTC, Manu wrote:
>
>> Surely the term you're looking for here is @annotate(...) ?
>>
>
> Meh, I don't care that much about names. I went
> with "note" anticipating people would complain about
> @add_user_defined_attribute() being too long :)
>
>
>
> What if you want to annotate with a variable?
>>
>
> That *might* work because a variable is an expression too. I'm not sure though. Will probably have to implement to know for sure.
>
> Of course, a constant can be represented as a struct
> for name and value:
>
> struct Description { string s; }
>
> @note(Description("yada yada yada")) int a;
>
>
>
> Surely this is just as easy: @modulename.attribute int myThing;
>>
>
> Yeah, I think so. I remember this being a counterpoint
> last time we talked about it, but I don't recall the
> specific argument made.
>
>
> Perhaps that's the key distinction between 'annotation' and a 'custom
>> attributes' .. an annotation this way is a shared compile time constant, associating with its type info, as you suggest. A custom attribute is an instance-specific association vontaining variable data.
>>
>
> Yeah, "annotation" might be the better word. That's
> what I want here, but too late to change the subject name
> now.
>
>
> attribute myAttribute
>>
>
> I think this could be done as a struct, but I haven't
> used this kind of attribute at all so not sure...
>
> But what I'm thinking is:
>
> struct myAttribute(Type) {
> Type value;
> alias value this;
>
> bool bNeedsAttention;
> @property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; }
> }
>
> myAttribute!int thing;
> thing.refresh = true;
>
> and thing can be substituted for an int anywhere else.
>
Interesting approach, how will that affect 'thing's type?
|
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 16 March 2012 at 15:10:06 UTC, Manu wrote:
> Interesting approach, how will that affect 'thing's type?
It will strictly be myAttribute!int, but alias this
means that you can pass it anywhere an int is expected
too (and assign ints to it all the same).
Check it:
void cool(int a) {}
void main() {
myAttribute!int thing;
thing.refresh = true;
thing = 10; // we can assign ints to it, like if it was an int
assert(thing.bNeedsAttention); // should be unchanged
int a = thing; // no problem, thing is usable as an int
assert(a == 10);
cool(thing); // ditto
}
|
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe Attachments:
| On 16 March 2012 17:14, Adam D. Ruppe <destructionator@gmail.com> wrote:
> On Friday, 16 March 2012 at 15:10:06 UTC, Manu wrote:
>
>> Interesting approach, how will that affect 'thing's type?
>>
>
> It will strictly be myAttribute!int, but alias this
> means that you can pass it anywhere an int is expected
> too (and assign ints to it all the same).
>
> Check it:
>
> void cool(int a) {}
> void main() {
>
> myAttribute!int thing;
> thing.refresh = true;
>
> thing = 10; // we can assign ints to it, like if it was an int
> assert(thing.bNeedsAttention); // should be unchanged
>
> int a = thing; // no problem, thing is usable as an int
> assert(a == 10);
>
> cool(thing); // ditto
> }
>
I can see that it might work nicely in very simple cases, but it could get nasty in complex constructs. If some template tries to take the typeof for instance, now I'm cloning the attributes functionality too, which I don't think is desirable...
|
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | Adam D. Ruppe wrote: > On the ride over here today, I had a thought that > I think neatly solves the user defined attribute > question. > > enum Serializable { yes, no } > > @note(Serializable.yes) int a; This is just enum. If you use struct or class attributes (like in C#) then direct @Struct(constructor args..) might be better. Moreover, structs and classes may have special member which validates attribute target: struct MyAttribute { template canAttach(alias Symbol) { // attribute is only valid on structs and // may be used only once per symbol enum canAttach = is(Symbol == struct) && !__traits(hasNotes, Symbol); } } It should be automatically evaluated by the compiler after attribute is attached. Alternatively it may be written like this: template validate(alias Symbol) { static assert(is(Symbol == struct), "MyAttribute must be used with structs"); } > We introduce two new things to the language: > > 1) the @note(expression) attribute. You can put > as many "notes" on a declaration as you want by > simply listing them. > > The expression inside is appended to a list on > the declaration. > > This would be valid on variables, functions, > function parameters, struct members, etc. > > 2) __traits(getNotes, decl). This returns a tuple > of all the note expressions. > > foreach(i, exp; __traits(getNotes, a)) { > static assert(is(typeof(exp) == Serializable); > static assert(exp == Serializable.yes); > } This is nice. Also it's base for library functions like hasNotes or getNotes!(Symbol, MyAttribute). > This simple extension to the language enables libraries, > using existing traits to navigate symbols, to get > additional information about things and implement it > however. > > The lack of user defined attributes is D's biggest missed > opportunity right now, and I think this simple proposal > will plug that hole. Yes, it's big drawback for serialization and data binding code. > Previous user-defined attribute proposals have had counter > arguments like these: > > 1) how do you define what is and is not a valid attribute? > > Here, the answer is pretty simple: you can put whatever notes > you want on the thing. Whether the library uses it or not > is up to it. > > It has to be a valid type - so the compiler will catch > typos and whatnot - but it doesn't have to be used unless > you ask for it. It should be any user-defined type that may be used at compile-time. For example @UUID("...") should be available out of the box. > 2) OK, how do you namespace things so different libraries don't > get different results? > > That's where the beauty of the expression type comes in: D > already has namespaces. foo.Serializable != bar.Serializable, > so there's no conflict. > > We simply declare types. The enum X {yes, no} pattern is already > common in Phobos, and is also the best way to express a bool condition > here. (Technically, @note(true) would compile, but it'd be much less > useful than declaring a name for the note too, with an enum.) > > Name conflicts is a problem already solved by the language. Yes, it should be possible to write @std.UUID(""). > 3) Can we have shared attributes, with declared names so people > know what to use in their own libraries? Of course, for example consider a validating attribute "Flags": @Flags enum SomeFlags { a = 1, b = 2, c = 4, d = 8 } During attachment it would check if enum member's bits don't overlap. |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On 3/16/12 8:35 AM, Adam D. Ruppe wrote: > enum Serializable { yes, no } > > @note(Serializable.yes) int a; [...] > foreach(i, exp; __traits(getNotes, a)) { > static assert(is(typeof(exp) == Serializable); > static assert(exp == Serializable.yes); > } So we have: class A { @note(Serializable.yes) int a; ... } vs. a hypothetical in-language solution: class A { int a; mixin(note("a", Serializable.yes)); ... } I wonder to what extent the in-language solution can be made to work. Andrei |
March 16, 2012 Re: Proposal: user defined attributes | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Friday, 16 March 2012 at 16:09:55 UTC, Andrei Alexandrescu wrote: > I wonder to what extent the in-language solution can be made to work. It can sort of work, but it isn't very good. (I thought you might say this too; check out this post: http://forum.dlang.org/thread/bccwycoexxykfgxvedix@forum.dlang.org#post-gmtawdmaihzgxnvezfrf:40forum.dlang.org ) In that other post, I talked about function parameters (which is what I really want it for). The in-language solution for that can only be made to work with a pile of fragile hacks. But, even for functions, you just wrote: int a; mixin(note("a", Serializable.yes)); OK, that works... but what about: int a() { return 0; } mixin(note("a", Serializable.yes)); We're good so far... until: int a() { return 0; } int a(int b) { return 0; } mixin(note("a", Serializable.yes)); // which a? You could potentially solve that by using the mangle of instead of the string, and doing an alias template rather than passing a string directly, but I'm not sure if we actually can in today's dmd (I don't even know how to refer to the proper overload...) Also, what if the declaration is anonymous? You could still see it in reflection - a ParameterTypeTuple doesn't care about names - but you couldn't annotate it this way. I guess a workaround there would be giving it a name like _param_0. Moreover, the mixin adds some junk to the struct. If you iterate over allMembers, will you see enum _note_a_serializable; in there? If there's a consistent naming convention, we could skip it (my web.d ignores everything with a leading underscore, for example), but it is nevertheless letting implementation details slip out in reflection. Seeing how the whole point of this is to be used in reflection, that's a case I think is likely to cause annoyance in practice. The current language solution isn't really *bad* with enough library help, but it isn't particularly *good* either and I don't think it can be. I've tried a few things, and I still see the lack of user annotations as D's biggest miss right now. |
Copyright © 1999-2021 by the D Language Foundation