Jump to page: 1 28  
Page
Thread overview
custom attribute proposal (yeah, another one)
Apr 06, 2012
Bernard Helyer
Apr 06, 2012
Adam D. Ruppe
Apr 06, 2012
Timon Gehr
Apr 06, 2012
Manu
Apr 06, 2012
Adam D. Ruppe
Apr 06, 2012
Manu
Apr 06, 2012
Manu
Apr 06, 2012
Timon Gehr
Apr 06, 2012
Manu
Apr 06, 2012
Adam D. Ruppe
Apr 06, 2012
Manu
Apr 06, 2012
Mafi
Apr 06, 2012
Mafi
Apr 07, 2012
deadalnix
Apr 07, 2012
Jacob Carlborg
Apr 09, 2012
Jacob Carlborg
Apr 08, 2012
Marco Leise
Apr 06, 2012
Piotr Szturmaj
Apr 06, 2012
Andrej Mitrovic
Apr 06, 2012
Andrej Mitrovic
Apr 06, 2012
Piotr Szturmaj
Apr 06, 2012
Andrej Mitrovic
Apr 06, 2012
Piotr Szturmaj
Apr 07, 2012
Jacob Carlborg
Apr 09, 2012
Marco Leise
Apr 06, 2012
Piotr Szturmaj
Apr 06, 2012
Piotr Szturmaj
Apr 07, 2012
Jacob Carlborg
Apr 07, 2012
Piotr Szturmaj
Apr 07, 2012
Jacob Carlborg
Apr 07, 2012
Johannes Pfau
Apr 07, 2012
deadalnix
Apr 07, 2012
Jacob Carlborg
Apr 06, 2012
Tove
Apr 06, 2012
Tove
Apr 06, 2012
Piotr Szturmaj
Apr 06, 2012
Piotr Szturmaj
Apr 06, 2012
Timon Gehr
Apr 06, 2012
Manu
Apr 06, 2012
bls
Apr 06, 2012
bls
Apr 07, 2012
Jacob Carlborg
Apr 06, 2012
Dmitry Olshansky
Apr 06, 2012
Manu
Apr 06, 2012
David Gileadi
Apr 07, 2012
deadalnix
Apr 07, 2012
Kapps
Apr 07, 2012
deadalnix
Apr 07, 2012
deadalnix
Apr 07, 2012
Kapps
Apr 07, 2012
deadalnix
Apr 07, 2012
Jacob Carlborg
Apr 07, 2012
Manu
Apr 07, 2012
Kapps
Apr 07, 2012
Manu
Apr 07, 2012
Jacob Carlborg
Apr 09, 2012
Jacob Carlborg
Apr 08, 2012
foobar
April 06, 2012
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
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
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
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
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
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
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
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
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
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.
« First   ‹ Prev
1 2 3 4 5 6 7 8