Thread overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
April 06, 2012 custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
OK, so I woke up this morning to find a huge discussion on attributes, and I'd like to once again propose the idea of how to define and use an attribute. I do not like the idea of: @attr(identifier) Why? Because what the hell did we create that "@" syntax for? I though it was to avoid misinterpreting such things as normal symbols, and avoid creating new keywords. Why should the compiler be the only one able to use such symbols? Another thing I don't like is some idea that only a certain type of construct can be the identifier. An attribute should have one requirement -- that it can be created/determined at compile time. So here is my proposal: 1. Introduce a new compiler-defined attribute @attribute (or @attr or something better, the name isn't important). 2. This attribute can *only* be used on a module-level function. 3. @attribute functions *must* be CTFE-able. 4. An @attribute function can be used as a user-defined attribute on any declaration using the syntax @identifier where identifier is the name of the attribute function (subject to normal function lookup rules). If the attribute can be called without parameters, the parentheses are optional. 5. When used on a declaration, that CTFE function is called during compile-time, and the result of that function is stored as metadata on that symbol. It does not affect the type of the symbol or transfer to any other symbols that are assigned to the value of that declaration (in other words, it *cannot* be used as a type constructor). 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. 7. One can lookup whether an attribute exists on a symbol using __traits(hasAttribute, symbol). 8. One can retrieve the value of the CTFE result using __traits(getAttribute, symbol). If the CTFE function returns void, this is a compiler error. And that's it. We can extend this eventually to storing something in TypeInfo, but I'm not sure we need that. However, I want to stress that having runtime type metadata is not a requirement for this proposal. Example usage: @attribute bool serializable(bool yesorno = true) { return yesorno; } unittest { // serializable is a normal function also assert(serializable() == true); assert(serializable(true) == true); assert(serializable(false) == false); } @serializable struct MyType { int x; int y; @serializable(false) int z; } string serialize(T)(const ref T t) if (__traits(hasAttribute, serializable) && __traits(getAttribute, serializable)) { // serialize each field. Skip any fields that are marked as serializable == false } -Steve |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | I think this is the sanest proposal I've seen yet. It leverages what it needs (CTFE, module look up) without being incomprehensible, and is usable at compile time. Two thumbs up. |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 6 April 2012 at 13:23:03 UTC, Steven Schveighoffer wrote: > So here is my proposal: This is a pretty good one. I can live with it. Two notes though: > 1. Introduce a new compiler-defined attribute @attribute (or @attr or something better, the name isn't important). > 2. This attribute can *only* be used on a module-level function. This is a fine time to disallow built-in attribute identifiers. safe is not a keyword: int safe() { return 0; } void main() { if(safe) assert(0); } That's valid code. But, @safe already has a meaning. While int safe() is fine, @atttribute int safe() shouldn't be. If it throws an error when it sees that declaration, we're in business. @attribute int safe() Error: attribute identifer safe is reserved for the compiler > 3. @attribute functions *must* be CTFE-able. Can this be statically checked? Since CTFE-able-ness depends on runtime params and errors, I don't think so. I'd say this should not be a strict requirement on the declaration. Just let the CTFE fail when you try to use it. |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Fri, 06 Apr 2012 09:23:01 -0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote: > 7. One can lookup whether an attribute exists on a symbol using __traits(hasAttribute, symbol). > 8. One can retrieve the value of the CTFE result using __traits(getAttribute, symbol). If the CTFE function returns void, this is a compiler error. Boy, this was underspecified! __traits(hasAttribute, attribute, symbol) __traits(getAttribute, attribute, symbol) > string serialize(T)(const ref T t) if (__traits(hasAttribute, serializable) && __traits(getAttribute, serializable)) again: if (__traits(hasAttribute, serializable, T) && __traits(getAttribute, serializable, T)) -Steve |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Adam D. Ruppe | On Fri, 06 Apr 2012 09:43:37 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote: > On Friday, 6 April 2012 at 13:23:03 UTC, Steven Schveighoffer wrote: >> 1. Introduce a new compiler-defined attribute @attribute (or @attr or something better, the name isn't important). >> 2. This attribute can *only* be used on a module-level function. > > This is a fine time to disallow built-in attribute identifiers. > > safe is not a keyword: > > int safe() { return 0; } > void main() { if(safe) assert(0); } > > That's valid code. > > But, @safe already has a meaning. > > While int safe() is fine, @atttribute int safe() shouldn't > be. > > If it throws an error when it sees that declaration, we're > in business. > > @attribute int safe() > Error: attribute identifer safe is reserved for the compiler Good point, I agree. >> 3. @attribute functions *must* be CTFE-able. > > Can this be statically checked? Since CTFE-able-ness > depends on runtime params and errors, I don't think so. I thought there were two parts to CTFE: 1. The parameters can be determined at compile-time 2. The function has certain attributes (e.g. all source code available, does not access globals, etc.) I really was referring to part 2, which I assumed was statically determined. This differs from current CTFE-able functions in that the compiler doesn't know at declaration whether it's going to be used for CTFE or not. > I'd say this should not be a strict requirement on > the declaration. Just let the CTFE fail when you try > to use it. If this is the only way to do it, I'm fine with that. -Steve |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | I think this proposal should be merged with Johannes' one.
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 = 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.
|
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr | On Fri, 06 Apr 2012 09:53:59 -0400, Timon Gehr <timon.gehr@gmx.ch> wrote:
> I think this proposal should be merged with Johannes' one.
It is very similar. I think the main distinction is that I focused on the fact that the compiler already has a mechanism to check and run CTFE functions.
-Steve
|
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Timon Gehr Attachments:
| On 6 April 2012 16:53, Timon Gehr <timon.gehr@gmx.ch> wrote: > I think this proposal should be merged with Johannes' one. > 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 = 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. >> > |
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer Attachments:
| On 6 April 2012 16:56, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> On Fri, 06 Apr 2012 09:53:59 -0400, Timon Gehr <timon.gehr@gmx.ch> wrote:
>
> I think this proposal should be merged with Johannes' one.
>>
>
> It is very similar. I think the main distinction is that I focused on the fact that the compiler already has a mechanism to check and run CTFE functions.
>
Except you're using a function, which I don't follow. How does that work?
Where do you actually store the attribute data?
Just attaching any arbitrary thing, in particular, a struct (as in Johannes
proposal) is far more useful. It also seems much simpler conceptually to
me. It's nice when things are intuitive...
|
April 06, 2012 Re: custom attribute proposal (yeah, another one) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Manu | On Friday, 6 April 2012 at 14:11:42 UTC, Manu wrote:
> Except you're using a function, which I don't follow.
It is pretty simple: the return value of the function
is stored in the compiler, the same as all the other
proposals.
The struct thing is the same, really. You're just calling
a constructor there instead of a regular function.
Really, of the... what five proposals out there now? But
they are all almost the same. A constructor, a function
call, an expression, or a field initializor list all
give the same result - they all return a piece of data,
which is attached to the declaration in the compiler.
We're just quibbling over details. I say we just forget
about that and pick one to make this happen.
I barely even care which one right now.
|
Copyright © 1999-2021 by the D Language Foundation