www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - User Defined Attributes (UDA) in Phobos/druntime

reply Jacob Carlborg <doob me.com> writes:
This was brought up in the thread reviewing std.serialization:

http://forum.dlang.org/thread/adyanbsdsxsfdpvoozne forum.dlang.org

Currently we haven't started to use UDA's in Phobos or druntime yet (as 
far as I know). I would like to start a discussion about putting out 
some guidelines for using UDA's in Phobos/druntime.

I've created a small module that handles UDA's:

https://github.com/jacob-carlborg/orange/blob/master/orange/core/Attribute.d

The idea is to have a kind of meta UDA called "attribute". This 
attribute needs to be attached to all structs, enums and so on that is 
to be used as an attribute.

The module provides a template (getAttributes) to get, by default, only 
those values attached to a given symbol that is an attribute. That is, 
marked with the "attribute" attribute.

So my suggestion for guidelines are:

* Only types with the "attribute" UDA is to be used as attributes
* In general, don't use primitive values as a UDA

Don't use this

 (3) int a;

Use this:

 attribute struct foo { int b; }

 foo(3) int a;

* A user defined type marked with "attribute" should not be used for 
something else than an UDA

* All attributes use camel case names

If we agree on this and that we need a module like the one above I think 
it should be added to druntime. The reason for that is that we most 
likely want to use UDA's in druntime and not only in Phobos. Example, if 
we ever get std.serialization into Phobos we would want to mark Thread 
and similar structs/classes as "nonSerialized".

Thoughts?

-- 
/Jacob Carlborg
Jun 11 2013
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
Jun 11 2013
next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
Wise words. +1
Jun 11 2013
prev sibling next sibling parent reply "Jonas Drewsen" <nospam4321 hotmail.com > writes:
On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
Though I might not agree with this suggested meta-attribute thing I really think that serialization screams for using attributes to mark fields/classes as serializable. Would be sad not to have them. Jacob: Would it be doable to refactor the attribute magic in std.serialization into a separate module that could be provided as 3rd party extension to std.serialization until phobos itself is ready for attributes? /Jonas
Jun 11 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-06-11 11:07, Jonas Drewsen wrote:

 Jacob: Would it be doable to refactor the attribute magic in
 std.serialization into a separate module that could be provided as 3rd
 party extension to std.serialization until phobos itself is ready for
 attributes?
The generic code for attributes is located in orange.core.Attribute, this can be inlined or removed. The actual attributes them self are located in orange.serialization.Serializable. The library is perfectly usable without UDA's. The old template mixins are still available. If __traits(getAttributes) is not allowed in Phobos then I have to remove the code that checks for UDA's. -- /Jacob Carlborg
Jun 11 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-06-11 11:58, Jacob Carlborg wrote:

 The library is perfectly usable without UDA's. The old template mixins
 are still available.
The template mixins basically emulates UDA's. -- /Jacob Carlborg
Jun 11 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 11 Jun 2013 05:58:50 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2013-06-11 11:07, Jonas Drewsen wrote:

 Jacob: Would it be doable to refactor the attribute magic in
 std.serialization into a separate module that could be provided as 3rd
 party extension to std.serialization until phobos itself is ready for
 attributes?
The generic code for attributes is located in orange.core.Attribute, this can be inlined or removed. The actual attributes them self are located in orange.serialization.Serializable. The library is perfectly usable without UDA's. The old template mixins are still available. If __traits(getAttributes) is not allowed in Phobos then I have to remove the code that checks for UDA's.
I think we can have and use UDAs in Phobos without yet having to create a guideline. Note that there is lots of experience with attributes in other languages that we can draw from. Most people here have written in at least one other language that uses them. I have no problem with std.serialization using attributes, even if there is no "official" guide. -Steve
Jun 11 2013
prev sibling next sibling parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
I disagree - if you wait for UDAs to appear more in user-code, you'll end up with multiple conventions for using them. If they are used in Phobos/druntime first, users will conform to the Phobos/druntime convention.
Jun 11 2013
parent "John Colvin" <john.loughran.colvin gmail.com> writes:
On Tuesday, 11 June 2013 at 14:59:44 UTC, Idan Arye wrote:
 On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
I disagree - if you wait for UDAs to appear more in user-code, you'll end up with multiple conventions for using them.
That's what we want, surely: Multiple conventions appear, then with the benefit of hindsight the best can be selected for phobos.
Jun 11 2013
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
That sound good, but the complete opposite have been done every single time. That raise the question of consistency.
Jun 12 2013
parent "SomeDude" <lovelydear mailmetrash.com> writes:
On Wednesday, 12 June 2013 at 07:46:31 UTC, deadalnix wrote:
 On Tuesday, 11 June 2013 at 07:36:44 UTC, Walter Bright wrote:
 On 6/11/2013 12:00 AM, Jacob Carlborg wrote:
 Thoughts?
I'd like to see more use of UDAs in non-Phobos code so we can figure out best practices from experience before putting it into Phobos, where we'll be stuck with any bad decisions.
That sound good, but the complete opposite have been done every single time. That raise the question of consistency.
And that's why Walter wants to avoid repeating the same mistakes again.
Jun 12 2013
prev sibling next sibling parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Tuesday, 11 June 2013 at 07:00:29 UTC, Jacob Carlborg wrote:
 The idea is to have a kind of meta UDA called "attribute". This 
 attribute needs to be attached to all structs, enums and so on 
 that is to be used as an attribute.

 The module provides a template (getAttributes) to get, by 
 default, only those values attached to a given symbol that is 
 an attribute. That is, marked with the "attribute" attribute.

 So my suggestion for guidelines are:

 * Only types with the "attribute" UDA is to be used as 
 attributes
 * In general, don't use primitive values as a UDA

 Don't use this

  (3) int a;

 Use this:

  attribute struct foo { int b; }

  foo(3) int a;

 * A user defined type marked with "attribute" should not be 
 used for something else than an UDA

 * All attributes use camel case names
Camel case has two versions - and I suggest to use the one that starts with capital - that is, ` attribute struct FooBar` and not ` attribute struct fooBar`. This will save a lot of trouble when we want to add new keyword attributes. The exception is the `attribute` attribute itself - in the future we might want to make it a keyword attribute(so we can, for example, enforce it's usage for attributes), and it that case it would be helpful if all UDAs already use it.
Jun 11 2013
parent "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Tuesday, June 11, 2013 16:48:13 Idan Arye wrote:
 Camel case has two versions - and I suggest to use the one that
 starts with capital
The term pascal case is frequently used when referring to the variant of camelcase which starts with a capital letter. - Jonathan M Davis
Jun 11 2013
prev sibling parent reply "QAston" <qaston gmail.com> writes:
On Tuesday, 11 June 2013 at 07:00:29 UTC, Jacob Carlborg wrote:
 This was brought up in the thread reviewing std.serialization:

 http://forum.dlang.org/thread/adyanbsdsxsfdpvoozne forum.dlang.org

 Currently we haven't started to use UDA's in Phobos or druntime 
 yet (as far as I know). I would like to start a discussion 
 about putting out some guidelines for using UDA's in 
 Phobos/druntime.

 I've created a small module that handles UDA's:

 https://github.com/jacob-carlborg/orange/blob/master/orange/core/Attribute.d

 The idea is to have a kind of meta UDA called "attribute". This 
 attribute needs to be attached to all structs, enums and so on 
 that is to be used as an attribute.

 The module provides a template (getAttributes) to get, by 
 default, only those values attached to a given symbol that is 
 an attribute. That is, marked with the "attribute" attribute.

 So my suggestion for guidelines are:

 * Only types with the "attribute" UDA is to be used as 
 attributes
 * In general, don't use primitive values as a UDA

 Don't use this

  (3) int a;

 Use this:

  attribute struct foo { int b; }

  foo(3) int a;

 * A user defined type marked with "attribute" should not be 
 used for something else than an UDA

 * All attributes use camel case names

 If we agree on this and that we need a module like the one 
 above I think it should be added to druntime. The reason for 
 that is that we most likely want to use UDA's in druntime and 
 not only in Phobos. Example, if we ever get std.serialization 
 into Phobos we would want to mark Thread and similar 
 structs/classes as "nonSerialized".

 Thoughts?
I agree that attributes should have types - that way it's easily recognizable what are they for in code. "Anonymous" attributes seem to me to be sort of like "you can throw ANYTHING in c++" feature - it's there, but probably without a sane use case. Could you explain to me what's the benefit of the attribute convention you introduce? It seems non-obvious to me.
Jun 11 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-06-11 18:12, QAston wrote:

 I agree that attributes should have types - that way it's easily
 recognizable what are they for in code. "Anonymous" attributes seem to
 me to be sort of like "you can throw ANYTHING in c++" feature - it's
 there, but probably without a sane use case.
 Could you explain to me what's the benefit of the  attribute convention
 you introduce? It seems non-obvious to me.
It shows the intent of the type. D both have a keywords to indicate an interface and an abstract class. In C++ interfaces and abstract classes are possible as well, but there's no real way to tell that a given class is actually supposed to be used as an interface. I was kind of disappointed with the way D implemented UDA's. Just dump any value/type to a symbol. -- /Jacob Carlborg
Jun 11 2013
next sibling parent reply "QAston" <qaston gmail.com> writes:
On Tuesday, 11 June 2013 at 20:31:22 UTC, Jacob Carlborg wrote:
 On 2013-06-11 18:12, QAston wrote:

 I agree that attributes should have types - that way it's 
 easily
 recognizable what are they for in code. "Anonymous" attributes 
 seem to
 me to be sort of like "you can throw ANYTHING in c++" feature 
 - it's
 there, but probably without a sane use case.
 Could you explain to me what's the benefit of the  attribute 
 convention
 you introduce? It seems non-obvious to me.
It shows the intent of the type. D both have a keywords to indicate an interface and an abstract class. In C++ interfaces and abstract classes are possible as well, but there's no real way to tell that a given class is actually supposed to be used as an interface. I was kind of disappointed with the way D implemented UDA's. Just dump any value/type to a symbol.
Ok, i see the point now, thanks :). Maybe it'd be worth to enforce that convention on a language level, let's say: only types with attribute can be used as UDA. One reason for making that restriction is that when there's more than a one way of doing something people will do that using all the ways possible. This may be a problem to code which uses many libraries simultanously - your utility functions will not interoperate with UDAs made by someone else. BTW I've just found one use case for anonymous UDA: (Enum.Entry) is verbose, the question is: "Is it useful enough to keep it, or maybe having single convention is better?" Certainly, there's no need to have arbitrary types in attributes like UnawareClass because you can do TakeUnaware!(UnawareClass) instead. The other way around would be to operate on SerializationAttribute specific to the lib so you can fetch only the attributes you're interested in easily.
Jun 11 2013
next sibling parent reply "Idan Arye" <GenericNPC gmail.com> writes:
On Tuesday, 11 June 2013 at 21:04:40 UTC, QAston wrote:
 The other way around would be to operate on 
  SerializationAttribute specific to the lib so you can fetch 
 only the attributes you're interested in easily.
How about passing template arguments to ` attribute` to determine what libraries the UDA it declares belong to? Here is an example implementation: https://gist.github.com/someboddy/5762072
Jun 11 2013
parent "QAston" <qaston gmail.com> writes:
On Wednesday, 12 June 2013 at 00:42:24 UTC, Idan Arye wrote:
 How about passing template arguments to ` attribute` to 
 determine what libraries the UDA it declares belong to?

 Here is an example implementation: 
 https://gist.github.com/someboddy/5762072
Yes, I think something like this solves interoperation issues.
Jun 12 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-06-11 23:04, QAston wrote:

 Ok, i see the point now, thanks :). Maybe it'd be worth to enforce that
 convention on a language level, let's say: only types with  attribute
 can be used as UDA. One reason for making that restriction is that when
 there's more than a one way of doing something people will do that using
 all the ways possible. This may be a problem to code which uses many
 libraries simultanously - your utility functions will not interoperate
 with UDAs made by someone else.
Yes, I would like to have the language enforce this. My utility function fetches by default types that are marked with attribute. You can add flag when calling the function to fetch all UDA's.
 BTW I've just found one use case for anonymous UDA:
  (Enum.Entry) is verbose, the question is: "Is it useful enough to keep
 it, or maybe having single convention is better?"
Is it possible to attach a UDA to an enum member? Or is it possible to figure out that it's an enum member and check if the enum itself has attribute attached to it? -- /Jacob Carlborg
Jun 11 2013
parent "QAston" <qaston gmail.com> writes:
On Wednesday, 12 June 2013 at 06:46:06 UTC, Jacob Carlborg wrote:
 Yes, I would like to have the language enforce this. My utility 
 function fetches by default types that are marked with 
  attribute. You can add flag when calling the function to fetch 
 all UDA's.

 BTW I've just found one use case for anonymous UDA:
  (Enum.Entry) is verbose, the question is: "Is it useful 
 enough to keep
 it, or maybe having single convention is better?"
Is it possible to attach a UDA to an enum member? Or is it possible to figure out that it's an enum member and check if the enum itself has attribute attached to it?
Accrding to this http://dpaste.dzfl.pl/728872fa it's not possible to attach an UDA to enum member. I think that what you propose, together with parametrisation made by IdanArye to your attribute definition is a nice compromise for now. It allows reuse of utils and interoperation, sets up a convention to follow and does not enforce language changes. But it will only work if it's going to be in phobos for example as std.attribute. In my opinion such compromise would be easier to get into the language than fully enforcing the convention and I think it's very important to have some form of UDAs for serialization just for users' convenience.
Jun 12 2013
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 6/11/13, Jacob Carlborg <doob me.com> wrote:
 I was kind of disappointed with the way D implemented UDA's. Just dump
 any value/type to a symbol.
But without this you lose the ability to customize. I don't like just "tagging" something, I like D's ability where you can actually customize the UDA, for example: ----- import std.typetuple; struct Server { string url; } struct Config { (Server("http://foo.bar")) string name; (Server("http://doo.bar")) string[] items; } template GetAttributes(T...) if (T.length == 1) { alias GetAttributes = TypeTuple!(__traits(getAttributes, T[0])); } void main() { Config config; alias attribs = GetAttributes!(config.name); static if (attribs.length && is(typeof(attribs[0]) == test.Server)) { pragma(msg, attribs[0]); } } ----- Thanks to CTFE we can tag a field with a struct that may have any state. All you have to do in the library code is to first check if any of the attributes is an "attribute type (a struct of some sort)" that you recognize, and then read its state.
Jun 12 2013
next sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 12 June 2013 at 12:29:53 UTC, Andrej Mitrovic wrote:
 On 6/11/13, Jacob Carlborg <doob me.com> wrote:
 I was kind of disappointed with the way D implemented UDA's. 
 Just dump
 any value/type to a symbol.
But without this you lose the ability to customize. I don't like just "tagging" something, I like D's ability where you can actually customize the UDA, for example:
Both are 100% orthogonal
Jun 12 2013
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-06-12 14:29, Andrej Mitrovic wrote:

 But without this you lose the ability to customize. I don't like just
 "tagging" something, I like D's ability where you can actually
 customize the UDA, for example:

 -----
 import std.typetuple;

 struct Server
 {
      string url;
 }

 struct Config
 {
       (Server("http://foo.bar")) string name;
       (Server("http://doo.bar")) string[] items;
 }

 template GetAttributes(T...) if (T.length == 1)
 {
      alias GetAttributes = TypeTuple!(__traits(getAttributes, T[0]));
 }

 void main()
 {
      Config config;

      alias attribs = GetAttributes!(config.name);

      static if (attribs.length && is(typeof(attribs[0]) == test.Server))
      {
          pragma(msg, attribs[0]);
      }
 }
 -----

 Thanks to CTFE we can tag a field with a struct that may have any
 state. All you have to do in the library code is to first check if any
 of the attributes is an "attribute type (a struct of some sort)" that
 you recognize, and then read its state.
As far as I know other language allows UDA's to be of any type/value. with System.Attribute as the base class are used. -- /Jacob Carlborg
Jun 12 2013