www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: user defined attributes

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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?
Mar 16 2012
next sibling parent reply Manu <turkeyman gmail.com> writes:
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.
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;
Mar 16 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Mar 16 2012
parent reply Manu <turkeyman gmail.com> writes:
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?
Mar 16 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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 }
Mar 16 2012
parent Manu <turkeyman gmail.com> writes:
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...
Mar 16 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/16/2012 7:11 AM, Manu wrote:
 attribute myAttribute
 {
    this(int something);

    bool bNeedsAttention;

    property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; }
 }

  myAttribute(10) int thing;

 thing.refresh = true;
Under the hood, where would that per-instance data be stored? Compile-time annotations can be stored internally to the compiler, but per-instance runtime data? How is it connected to the instance? When is it constructed and destroyed?
Mar 17 2012
next sibling parent Manu <turkeyman gmail.com> writes:
On 17 March 2012 21:56, Walter Bright <newshound2 digitalmars.com> wrote:

 On 3/16/2012 7:11 AM, Manu wrote:

 attribute myAttribute
 {
   this(int something);

   bool bNeedsAttention;

   property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; }
 }

  myAttribute(10) int thing;

 thing.refresh = true;
Under the hood, where would that per-instance data be stored? Compile-time annotations can be stored internally to the compiler, but per-instance runtime data? How is it connected to the instance? When is it constructed and destroyed?
attributed member, or maybe separate them into an attribute section at the end of the class (to preserve structural layout). Construct along with the class, I suppose the moment would be after member initialisation, but before the constructor executes. I wonder if it should only be allowed on classes/class members... adding hidden data to a 'struct' almost defeats the purpose. Java doesn't have
Mar 17 2012
prev sibling parent reply Manu <turkeyman gmail.com> writes:
On 17 March 2012 23:52, Manu <turkeyman gmail.com> wrote:

 On 17 March 2012 21:56, Walter Bright <newshound2 digitalmars.com> wrote:

 On 3/16/2012 7:11 AM, Manu wrote:

 attribute myAttribute
 {
   this(int something);

   bool bNeedsAttention;

   property void refresh(bool bRefresh) { bNeedsAttention = bRefresh; }
 }

  myAttribute(10) int thing;

 thing.refresh = true;
Under the hood, where would that per-instance data be stored? Compile-time annotations can be stored internally to the compiler, but per-instance runtime data? How is it connected to the instance? When is it constructed and destroyed?
attributed member, or maybe separate them into an attribute section at the end of the class (to preserve structural layout). Construct along with the class, I suppose the moment would be after member initialisation, but before the constructor executes. I wonder if it should only be allowed on classes/class members... adding hidden data to a 'struct' almost defeats the purpose. Java doesn't have
I tend to think of 'struct's as 'passive', and compile-time annotations would provide me with every use case I can imagine for my own purposes in the case of structs. classes seem far more likely to need stateful attributes.
Mar 17 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 2:55 PM, Manu wrote:
 I tend to think of 'struct's as 'passive', and compile-time annotations would
 provide me with every use case I can imagine for my own purposes in the case of
 structs. classes seem far more likely to need stateful attributes.
I currently find the notion of runtime attribute state to be fraught with all kinds of unresolved and murky issues with no obvious answer: 1. construction/destruction 2. shared 3. const/immutable 4. thread locality 5. allocated statically or dynamically 6. shared library issues? If the attribute is code, when does that code get run? 1. on initialization 2. on termination 3. on read 4. on write 5. what about read/write?
Mar 17 2012
parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 03:10, Walter Bright <newshound2 digitalmars.com> wrote:

 On 3/17/2012 2:55 PM, Manu wrote:

 I tend to think of 'struct's as 'passive', and compile-time annotations
 would
 provide me with every use case I can imagine for my own purposes in the
 case of
 structs. classes seem far more likely to need stateful attributes.
I currently find the notion of runtime attribute state to be fraught with all kinds of unresolved and murky issues with no obvious answer: 1. construction/destruction
After initialisation, before construction. 2. shared

How is this any different than for the class its self?

3. const/immutable

Inherit these properties.

4. thread locality

Same as class.

5. allocated statically or dynamically

I'd say dynamically if restricted to classes, but some suggested the syntax
' attribute struct/class MyAttribute { }', which could clarify the intent.

6. shared library issues?

If extern(D), then there should be no problems. If extern(C), then the
attributes would just appear like structure members. Assuming the layout
was documented (ie, in-order of appearance, at the end of the class), C/C++
could include them at the end of the class definition, and access them
directly as if usual classes. All that would be lost is the implicit
(compile time) association between the attribute and its associated class
member.
I don't think it's fair to expect a novel language feature to be supported
by C/C++ though.


If the attribute is code, when does that code get run?
 1. on initialization
 2. on termination
 3. on read
 4. on write
 5. what about read/write?
set of issues associated.
Mar 17 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 6:39 PM, Manu wrote:

 of issues associated.
Mar 17 2012
next sibling parent reply Manu <turkeyman gmail.com> writes:
On 18 March 2012 03:48, Walter Bright <newshound2 digitalmars.com> wrote:

 On 3/17/2012 6:39 PM, Manu wrote:


 same set
 of issues associated.
types.
The threadlocal/shared stuff shouldn't be a problem, since the attribute instances are contained by the class its self, it just has the same locality properties. Can you imagine a case where that would be problematic?
Mar 17 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 7:04 PM, Manu wrote:
 On 18 March 2012 03:48, Walter Bright <newshound2 digitalmars.com
 <mailto:newshound2 digitalmars.com>> wrote:

     On 3/17/2012 6:39 PM, Manu wrote:


         same set
         of issues associated.



types.



No, it does not.
 How does RAII cause a problem here?
Generally, supporting it tends to permeate the semantics of the language. Simple things like copying a value onto the parameter stack entail all kinds of issues.
 The threadlocal/shared stuff shouldn't be a problem, since the attribute
 instances are contained by the class its self, it just has the same locality
 properties. Can you imagine a case where that would be problematic?
If it's shared, how is access to the data controlled if multiple threads access it at the same time?
Mar 17 2012
prev sibling next sibling parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Sunday, 18 March 2012 at 01:48:07 UTC, Walter Bright wrote:
 On 3/17/2012 6:39 PM, Manu wrote:

 precisely the same set
 of issues associated.
threadlocal/shared types.
It has threadlocal using the [ThreadLocal] attribute which gets implemented by the compiler. the class/struct/method/field/parameter. I don't see this being a problem. I can't think of good use cases where an attribute/annotation should be per-instance at all, particularly any. Honestly, most uses of attributes in D would be done at compile-time, and I think that's acceptable until/if runtime reflection is put in. If it is needed, it can live inside TypeInfo/MethodInfo, but is (imo) absolutely not needed on a per-instance basis. This is the job of fields or interfaces.
Mar 17 2012
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 8:04 PM, Kapps wrote:
 It has threadlocal using the [ThreadLocal] attribute which gets implemented by
 the compiler.
That's a storage class, not a type constructor. In D, threadlocal/shared is a difference in the type.

 class/struct/method/field/parameter. I don't see this being a problem. I can't
 think of good use cases where an attribute/annotation should be per-instance at

 have any. Honestly, most uses of attributes in D would be done at compile-time,
 and I think that's acceptable until/if runtime reflection is put in. If it is
 needed, it can live inside TypeInfo/MethodInfo, but is (imo) absolutely not
 needed on a per-instance basis. This is the job of fields or interfaces.
I also do not get why per-instance attributes even exist, as I agree that's what fields are for.
Mar 17 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
Walter, how do you feel about the compile time
annotation list?

I looked at implementing it, and it is a little
more involved than I thought (mainly in changing
parse.c to treat   things as more than just storage
classes), but still not terrible.
Mar 17 2012
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 8:12 PM, Adam D. Ruppe wrote:
 Walter, how do you feel about the compile time
 annotation list?
I feel it has merit, but I think it is possibly more complex than necessary. Note that I have never used user defined attributes, so I don't have experience to guide me on this.
Mar 18 2012
prev sibling parent reply "F i L" <witte2008 gmail.com> writes:
Walter Bright wrote:
 I also do not get why per-instance attributes even exist, as I 
 agree that's what fields are for.
Attributes are per-type (type properties, methods, etc), not (to my knowledge). According to http://msdn.microsoft.com/en-us/library/z919e8tw(v=vs.80).aspx attribute objects aren't constructed until reflected upon. I think attributes in combination with D's templates would be very useful: attribute class Cool { bool isCool; this(bool cool) { isCool = cool; } } class CoolClass(bool cool) { CoolType(cool); int type; } void main() { auto cc = new CoolClass!true(); if (cc.type Cool.isCool) { // (new Cool(true)).isCool // Do something cool... } } Or, if attributes could be applied to *any* type (enums, structs, variables, etc..), we could write this more simply: attribute bool isCool; class CoolClass(bool cool) { isCool = cool; int type; } void main() { auto cc = new CoolClass!true(); if (cc.type isCool) { // true // Do something cool... } } and of course, the compiler could make use of attribute metadata during codegen. Things like Garbage Collection and memory compaction attributes come to mind: class MemoryPool { GC.DontScan void*[] pool; Int.Fast int index; // replace stdint } Also, aliasing could be possible: Int.Fast alias int intf; Int.Least alias int intl;
Mar 17 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
F i L wrote:
     class CoolClass(bool cool)
     {
          CoolType(cool);
         int type;
     }
typo ** Cool(cool); plus, I think a better syntax for attributes might be to have an attribute keyword for definitions and only use " " for application: attribute struct CoolType { ... } attribute bool isCool; CoolType (isCool = true) string coolText;
Mar 17 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 9:12 PM, F i L wrote:
 Walter Bright wrote:
 I also do not get why per-instance attributes even exist, as I agree that's
 what fields are for.
Attributes are per-type (type properties, methods, etc), not per-instance, and http://msdn.microsoft.com/en-us/library/z919e8tw(v=vs.80).aspx attribute objects aren't constructed until reflected upon.
My impression is it is just obfuscation around a simple lazy initialization pattern. While I can see the abstraction usefulness of compile time attribute metadata, I am having a hard time seeing what the gain is with runtime attributes over more traditional techniques.
Mar 17 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
Walter Bright wrote:
 My impression is it is just obfuscation around a simple lazy 
 initialization pattern.

 While I can see the abstraction usefulness of compile time 
 attribute metadata, I am having a hard time seeing what the 
 gain is with runtime attributes over more traditional 
 techniques.
I'm not sure exactly what you mean by runtime attributes. Do you mean the ability to reflect upon attributes at runtime? In that case, there's two benefits I can think of over "traditional" member data: Instance memory (attributes are create at reflection), and reusable metadata packages (similar to how you might use mixin templates only with less noise and reflection capabilities).
Mar 17 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/17/2012 10:01 PM, F i L wrote:
 Walter Bright wrote:
 My impression is it is just obfuscation around a simple lazy initialization
 pattern.

 While I can see the abstraction usefulness of compile time attribute metadata,
 I am having a hard time seeing what the gain is with runtime attributes over
 more traditional techniques.
I'm not sure exactly what you mean by runtime attributes. Do you mean the ability to reflect upon attributes at runtime?
I mean there is modifiable-at-runtime, instance-specific data.
 In that case, there's two
 benefits I can think of over "traditional" member data: Instance memory
 (attributes are create at reflection),
Lazy initialization is a standard pattern. No special language features are needed for it.
 and reusable metadata packages (similar
 to how you might use mixin templates only with less noise and reflection
 capabilities).
Sounds like a garden variety user-defined data type. Again, I am just not seeing the leap in power with this. It's a mystery to me how a user defined "attribute class" is different in any way from a user defined class, and what advantage special syntax gives a standard pattern for lazy initialization, and why extra attribute fields can't be just, well, fields.
Mar 18 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
Walter Bright wrote:
 I mean there is modifiable-at-runtime, instance-specific data.
(when gotten) from an Entity's metadata. No memory is stored per-instance unless you manage the objects manually: class A : Attribute { public string s = "Default"; } [TestA] class C {} static void Main() { // The line below is equivalent to: var a = new A(); // except that it's construction is defined by // metadata stored in type C. var a = typeof(C).GetCustomAttributes(true)[0] as A; a.s = "Modification"; Console.WriteLine(a.s); // prints "Modification" // Therefor... var b = typeof(C).GetCustomAttributes(true)[0] as A; Console.WriteLine(b.s); // prints "Default" }
 In that case, there's two
 benefits I can think of over "traditional" member data: 
 Instance memory
 (attributes are create at reflection),
Lazy initialization is a standard pattern. No special language features are needed for it.
I see how my statements (and code examples) where confusing. I meant that no attribute data is stored per-instance at all (unless traditionally done so), and that attribute objects are simply create in-place at the point of access. So to clarify my previous code a bit: attribute class A { string i = "Default"; } A class C { A a; } void main() { auto a = C A; // create new A based on C assert(is(typeof(a) : A)); // alternatively you could do: auto c = new C(); auto a = c A; // same as: typeof(c) A c.a = c A; // explicitly store attribute } Note: Might want to use the "new" keyword with class type attributes (auto a = new C A), but the idea's there. Plus, I
 and reusable metadata packages (similar
 to how you might use mixin templates only with less noise and 
 reflection capabilities).
Sounds like a garden variety user-defined data type.
It is. Only it's a data type who's construction values are stored in metadata (per entity), and therefor can be used at both compile and run times. By per-entity I mean for each unique Type, Type member, Sub-Type, etc. I don't know of any existing D idiom that is capable of what I presented. If there is, I would like to know. Here's the closest I can think of: mixin template CoolInt(bool cool, string name) { mixin("enum bool " ~ name ~ "_isCool = cool;"); mixin("int " ~ name ~ ";"); } class CoolClass { mixin CoolInt!(true, "a"); mixin CoolInt!(false, "b"); } void main() { auto c = new CoolClass(); writeln(c.a_isCool); // true writeln(c.b_isCool); // false } which, aside from noise, is great for runtime reflection, but it's completely useless (i think) for the compiler because the variables are created through arbitrary strings. Plus, I don't know how you'd store anything but simple variables, which more complex data would require a lot of <entity>_variables.
Mar 18 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
F i L wrote:
 Plus, I don't know how you'd store anything but simple 
 variables, which more complex data would require a lot of 
 <entity>_variables.
I found a way: mixin template Attribute( string type, string name, string attr, Params...) { mixin (type ~ " " ~ name ~ ";"); mixin ( "auto " ~ name ~ "_" ~ attr ~ "()" ~ "{ return new " ~ attr ~ "(Params); }" ); } class Cool { string s; this(string s) { this.s = s; } } class CoolClass { mixin Attribute!("int", "a", "Cool", "Heh"); mixin Attribute!("int", "b", "Cool", "Sup"); } void main() { auto c = new CoolClass(); writeln(c.a, ", ", c.b); // 0, 0 writeln(c.a_Cool().s); // Heh writeln(c.b_Cool().s); // Sup }
Mar 18 2012
parent reply "Tove" <tove fransson.se> writes:
On Sunday, 18 March 2012 at 10:25:20 UTC, F i L wrote:
 F i L wrote:
 class CoolClass
 {
     mixin Attribute!("int", "a", "Cool", "Heh");
     mixin Attribute!("int", "b", "Cool", "Sup");
 }

 void main()
 {
     auto c = new CoolClass();
     writeln(c.a, ", ", c.b); // 0, 0
     writeln(c.a_Cool().s); // Heh
     writeln(c.b_Cool().s); // Sup
 }
Is it not possible to alias a mixin to just one letter, and then use it to have any syntax we want... something like this: x(" attribute(Serializable.yes) int a");
Mar 18 2012
parent reply "F i L" <witte2008 gmail.com> writes:
On Sunday, 18 March 2012 at 10:38:19 UTC, Tove wrote:
 On Sunday, 18 March 2012 at 10:25:20 UTC, F i L wrote:
 F i L wrote:
 class CoolClass
 {
    mixin Attribute!("int", "a", "Cool", "Heh");
    mixin Attribute!("int", "b", "Cool", "Sup");
 }

 void main()
 {
    auto c = new CoolClass();
    writeln(c.a, ", ", c.b); // 0, 0
    writeln(c.a_Cool().s); // Heh
    writeln(c.b_Cool().s); // Sup
 }
Is it not possible to alias a mixin to just one letter, and then use it to have any syntax we want... something like this: x(" attribute(Serializable.yes) int a");
Sure, but there's still the issue of using attributes for codegen. For instance compare: struct Test { GC.NoScan int value; } to, the current: struct Test { int value; this() { GC.setAttr(&value, NO_SCAN); } } How can we do that with mixin templates? If attributes where a language type the compiler could exploit in a consistent way, It would be *trivial* describing this behavior in a declarative way.
Mar 18 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
F i L wrote:
     struct Test {
         int value;
         this() { GC.setAttr(&value, NO_SCAN); }
     }
bleh, should be... struct Test { int value; this(int v) { GC.setAttr(&value, GC.BlkAttr.NO_SCAN); } } Or something like that. I've never actually set GC attributes before. But this also raises another issue. Structs don't have default constructors, so applying NoScan attributes (by default) is, to my knowledge, impossible. Whereas it could (?) be possible through attributes.
Mar 18 2012
prev sibling parent reply "Tove" <tove fransson.se> writes:
On Sunday, 18 March 2012 at 10:50:14 UTC, F i L wrote:
 x(" attribute(Serializable.yes) int a");
Sure, but there's still the issue of using attributes for codegen. For instance compare: struct Test { GC.NoScan int value; } to, the current: struct Test { int value; this() { GC.setAttr(&value, NO_SCAN); } } How can we do that with mixin templates? If attributes where a language type the compiler could exploit in a consistent way, It would be *trivial* describing this behavior in a declarative way.
Hmm... well if the x declarations store all NoScan objects in a collection, it could be injected into the constructor token stream later... x!(" GC.NoScan int value;"); // modified by x to insert a foreach with GC.setAttr x!(q{this() {/* foreach(...) GC.setAttr(...); */ }); But I guess one lose the opportunity for some compile time magic... in a more efficient way than exposed by the GC.settAttr API.(just pure speculation, I don't have sufficient knowledge of the internal representation of our GC design.).
Mar 18 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Tove wrote:
 Hmm... well if the x declarations store all NoScan objects in a 
 collection, it could be injected into the constructor token 
 stream later...

 x!(" GC.NoScan int value;");

 // modified by x to insert a foreach with GC.setAttr
 x!(q{this() {/* foreach(...) GC.setAttr(...); */ });
But if x!() pumps out a constructor, how do you add multiple attributes with GC.NoScan? The constructors would collide.
Mar 18 2012
parent reply "Tove" <tove fransson.se> writes:
On Sunday, 18 March 2012 at 12:10:23 UTC, F i L wrote:
 Tove wrote:
 Hmm... well if the x declarations store all NoScan objects in 
 a collection, it could be injected into the constructor token 
 stream later...

 x!(" GC.NoScan int value;");

 // modified by x to insert a foreach with GC.setAttr
 x!(q{this() {/* foreach(...) GC.setAttr(...); */ });
But if x!() pumps out a constructor, how do you add multiple attributes with GC.NoScan? The constructors would collide.
1. x! would parse all decls at compile time... 2. all attributes that need to modify the constructor is inserted at the points where the x! enabled constructors are declared/implemented... x!(" GC.NoScan GC.Hot attribute(Serializable.yes) int value;"); x!(q{this() { /* everything from 'x' is auto inserted here */ my; normal; constructor; tokens; });
Mar 18 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Tove wrote:
 1. x! would parse all decls at compile time...
 2. all attributes that need to modify the constructor is 
 inserted at the points where the x! enabled constructors are 
 declared/implemented...

 x!(" GC.NoScan  GC.Hot  attribute(Serializable.yes) int 
 value;");

 x!(q{this()
 {
   /* everything from 'x' is auto inserted here */
   my;
   normal;
   constructor;
   tokens;
 });
I see, that would work. But why not just build this same operation into the compiler so the definition syntax is the same as usual. The mixin's are powerful but a bit ugly. Not to mention not IDE parser on the planet is going to be able to figure out all that to give you intelligent code-completion.
Mar 18 2012
parent "Tove" <tove fransson.se> writes:
On Sunday, 18 March 2012 at 12:39:19 UTC, F i L wrote:
 Tove wrote:
 1. x! would parse all decls at compile time...
 2. all attributes that need to modify the constructor is 
 inserted at the points where the x! enabled constructors are 
 declared/implemented...

 x!(" GC.NoScan  GC.Hot  attribute(Serializable.yes) int 
 value;");

 x!(q{this()
 {
  /* everything from 'x' is auto inserted here */
  my;
  normal;
  constructor;
  tokens;
 });
I see, that would work. But why not just build this same operation into the compiler so the definition syntax is the same as usual. The mixin's are powerful but a bit ugly. Not to mention not IDE parser on the planet is going to be able to figure out all that to give you intelligent code-completion.
Yes, I was thinking along these lines... what would be the absolutely bare minimum needed support this from the compiler to make this "scheme" look first class? what if... the compiler encounters an unknown token then it would delegate the parsing to a library implementation... which basically would do the above, but it would be hidden from the user... this way we would get a 0 effort(from the perspective of the compiler) and extensible syntax in library covering all future needs.
Mar 18 2012
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/18/2012 2:47 AM, F i L wrote:
 Walter Bright wrote:
 I mean there is modifiable-at-runtime, instance-specific data.
from an Entity's metadata. No memory is stored per-instance unless you manage the objects manually: class A : Attribute { public string s = "Default"; } [TestA] class C {} static void Main() { // The line below is equivalent to: var a = new A(); // except that it's construction is defined by // metadata stored in type C. var a = typeof(C).GetCustomAttributes(true)[0] as A; a.s = "Modification"; Console.WriteLine(a.s); // prints "Modification" // Therefor... var b = typeof(C).GetCustomAttributes(true)[0] as A; Console.WriteLine(b.s); // prints "Default" }
Which looks indistinguishable from modifiable at runtime, instance specific data.
 Lazy initialization is a standard pattern. No special language features are
 needed for it.
I see how my statements (and code examples) where confusing. I meant that no attribute data is stored per-instance at all (unless traditionally done so), and that attribute objects are simply create in-place at the point of access. So to clarify my previous code a bit: attribute class A { string i = "Default"; } A class C { A a; } void main() { auto a = C A; // create new A based on C assert(is(typeof(a) : A)); // alternatively you could do: auto c = new C(); auto a = c A; // same as: typeof(c) A c.a = c A; // explicitly store attribute } Note: Might want to use the "new" keyword with class type attributes (auto a = new C A), but the idea's there. Plus, I think that looks a lot better than the
Sorry, it still looks like standard lazy initialization. I don't know what attributes add to the party.
 Sounds like a garden variety user-defined data type.
It is. Only it's a data type who's construction values are stored in metadata (per entity), and therefor can be used at both compile and run times. By per-entity I mean for each unique Type, Type member, Sub-Type, etc. I don't know of any existing D idiom that is capable of what I presented.
Simply use a normal class. Instantiate it at runtime as needed.
 which, aside from noise, is great for runtime reflection, but it's completely
 useless (i think) for the compiler because the variables are created through
 arbitrary strings. Plus, I don't know how you'd store anything but simple
 variables, which more complex data would require a lot of <entity>_variables.
Something like Jacob's proposal for compile time attributes would be useful here. My failure to understand is about runtime attributes.
Mar 18 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-18 20:26, Walter Bright wrote:

 Something like Jacob's proposal for compile time attributes would be
 useful here. My failure to understand is about runtime attributes.
If we'll get user defined attributes that is accessible via runtime reflection I'm sure people will find uses cases for it that are not possible with compile time reflection. But currently I don't see any use cases for attributes per instance. If I'm thinking correctly this can be used for serializing. Say that you want to serialize an object through a base class reference. Currently you will need to register the subclass using some mechanism due to the limited runtime reflection in D. But if it would be possible to enumerate all fields in a class and also set and get the values of the fields using runtime reflection it wouldn't be necessary to register the subclass. Ok, now to the user defined attribute: attribute class NonSerialized {} // This is what's called a "marker" in Java. It doesn't contain any information, the information is the type itself. attribute class SerializeAs { string value; } class Base { int x; SerializeAs(foo) int y; NonSerialized int c; } NonSerialized class Sub : Base { int z; } Base sub = new Sub; serialize(sub); In this example the user defined attribute "NonSerialized" indicates that the class shouldn't be serialized. When the serializer is serializing "sub" the compile time type will be "Base" therefore it would need to use runtime reflection to also serialize the fields added in the subclass "Sub". To check if the object should be serialized the serializer need to be able to access user defined attributes using runtime reflection. Something like: auto classInfo = sub.classinfo; writeln(classInfo); // Sub if (classInfo.hasAttribute("NonSerialized")) // skip serialization else { foreach (field ; classInfo.fields) if (!field.hasAttribute("NonSerialized")) { auto value = filed.value; string name; if (attribute = filed.attributes["SerializeAs"]) name = attribute.value; else name = filed.name; // serialize the field } } We do all this during runtime because we want to serialize through base class references. If the runtime type is the same as the compile time type we can do this at compile time. -- /Jacob Carlborg
Mar 18 2012
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/18/2012 2:58 PM, Jacob Carlborg wrote:
 Ok, now to the user defined attribute:

  attribute class NonSerialized {} // This is what's called a "marker" in Java.
 It doesn't contain any information, the information is the type itself.

  attribute class SerializeAs
 {
 string value;
 }

 class Base
 {
 int x;
  SerializeAs(foo) int y;
  NonSerialized int c;
 }

  NonSerialized class Sub : Base
 {
 int z;
 }

 Base sub = new Sub;
 serialize(sub);

 In this example the user defined attribute "NonSerialized" indicates that the
 class shouldn't be serialized. When the serializer is serializing "sub" the
 compile time type will be "Base" therefore it would need to use runtime
 reflection to also serialize the fields added in the subclass "Sub". To check
if
 the object should be serialized the serializer need to be able to access user
 defined attributes using runtime reflection.
I'm sorry, I find this *massively* confusing. What is foo? Why are you serializing something marked "NonSerialized"? I really have no idea what is going on with this. What is the serialize() function? How does any of this tell you how to serialize an int? How is Base.c also a NonSerialized with c in a superclass of it? ?????????????????????????????????? Maybe this is why I don't much care for attributes - it's all just a fog to me what is happening.
Mar 18 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
Walter Bright wrote:
 I'm sorry, I find this *massively* confusing. What is foo? Why 
 are you serializing something marked "NonSerialized"? I really 
 have no idea what is going on with this. What is the 
 serialize() function?
He's giving an example of serialize() below the code you quoted. Walter Bright wrote:
 Which looks indistinguishable from modifiable at runtime,
 instance specific data.
If you're *only* considering the runtime ability of attributes, then it is. This: attribute struct A { this(int x, int y); void foo(); } A(1, 2) struct C {} void main() { auto a = C A; a.foo(); } would be conceptually equivalent to writing: struct A { this(int x, int y); void foo(); } struct C { static auto _A() { return A(1, 2); } } void main() { auto a = C._A(); a.foo(); } and it's my fault for not illustrating the important difference in the first place. Which is: Attributes are understandable by the compiler, therefor, attributes can describe behavior of an entity and inject code into key places where that entity is support Unions, however creating the structure is possible, like so: [StructLayout(LayoutKind.Explicit)] struct MyUnion { [FieldOffset(0)] int a; [FieldOffset(0)] long b; [FieldOffset(0)] float c; } Because Attributes are defined with unique syntax, they can be stored uniquely by the compiler, and then reacted upon _in a variety of ways_. They are available at runtime so that runtime code can react to them as well. To illustrate what Trove and I have been talking about, If I wanted to "mark" a variable so that the GC wouldn't scan it, I might be able to write something like: attribute class Memory { GC.BlkAttr blkAttr; this(GC.BlkAttr attr) { blkAttr = attr; } this(void* entity) { // the code in this function gets injected into // this() of the entity this class is attached to GC.setAttr(entity, blkAttr); } } The " this(void*)" function might always take in a void* (much like how new() always takes uint/size_t) which points to the entity it's attributing. So if we then write: Memory(GC.BlkAttr.NO_SCAN) string myString; Then when "myString.init()" is called, "GC.setAttr(myString, GC.BlkAttr.NO_SCAN)" will also be executed. I don't know enough about DMD's internals to know about how this would play out internally, but the concept is there. And I know my syntax has be changing over the last few posts (I've been going through different ideas), so I apologize if I'm being hard to follow here.
Mar 18 2012
prev sibling next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
Am 19.03.2012 01:41, schrieb Walter Bright:
 I'm sorry, I find this *massively* confusing. What is foo? Why are you
 serializing something marked "NonSerialized"? I really have no idea what is
 going on with this. What is the serialize() function? How does any of this tell
 you how to serialize an int? How is Base.c also a NonSerialized with c in a
 superclass of it?
attributes does not containing code - there just a (at-runtime) queryable information that can be attached to serveral things (like classes, methods, ...) - think of it like double.epsilon - but attribute-definitions tend to be something like an class (but without code) if something is annotated with an special attribute and use the configure information and do something with it - call an constructor, open an connection, generated code (at runtime) - whatever you want its a easy-to-use-buildin-attribution-system thats it - and people like attributes that are then used for stuff like serialization, memory-layout, ... a compiletime example of this could be: attribute my_special_attribute { int version; } attribute my_special_attribute2 { string test; } class test { [my_special_attribute(version=2)] int method1(); [my_special_attribute(version=2)] [my_special_attribute2(test="bert")] int method2(); } void main() { auto b = [ __traits(allMembers, D) ]; foreach( auto a; b ) { -->attribute query magic -->auto c = [ __traits(attribute("my_special_attribute", a) ]; -->auto c = [ __traits(attribute("my_special_attribute2", a) ]; //now we know all methods with my_special_attribute //and speical_special_attribute2 and their content (version=2 //and test="bert" //now think of an template or mixing that uses this //information for code-generation or something like that //thats all } }
Mar 18 2012
next sibling parent reply "Tove" <tove fransson.se> writes:
On Monday, 19 March 2012 at 06:46:09 UTC, dennis luehring wrote:
 Am 19.03.2012 01:41, schrieb Walter Bright:
 I'm sorry, I find this *massively* confusing. What is foo? Why 
 are you
 serializing something marked "NonSerialized"? I really have no 
 idea what is
 going on with this. What is the serialize() function? How does 
 any of this tell
 you how to serialize an int? How is Base.c also a 
 NonSerialized with c in a
 superclass of it?
attributes does not containing code - there just a (at-runtime) queryable information that can be attached to serveral things (like classes, methods, ...) - think of it like double.epsilon attribute-definitions tend to be something like an class (but without code) find out if something is annotated with an special attribute and use the configure information and do something with it - call an constructor, open an connection, generated code (at runtime) - whatever you want its a easy-to-use-buildin-attribution-system thats it - and developers a bunch of attributes that are then used for stuff like serialization, memory-layout, ... a compiletime example of this could be: attribute my_special_attribute { int version; } attribute my_special_attribute2 { string test; } class test { [my_special_attribute(version=2)] int method1(); [my_special_attribute(version=2)] [my_special_attribute2(test="bert")] int method2(); } void main() { auto b = [ __traits(allMembers, D) ]; foreach( auto a; b ) { -->attribute query magic -->auto c = [ __traits(attribute("my_special_attribute", a) ]; -->auto c = [ __traits(attribute("my_special_attribute2", a) ]; //now we know all methods with my_special_attribute //and speical_special_attribute2 and their content (version=2 //and test="bert" //now think of an template or mixing that uses this //information for code-generation or something like that //thats all } }
because of D:s CTFE... by introducing a call back from the D compiler to the library CTFE attribute handler... this way we can hide all reflection from the "end user"... and the compiler doesn't need to know anything at all, as the library does the semantic lowering. GC.NoScan int value; GC this() {} Compiler Asks library for transformation of unknown GC bool library(" GC this() {anything...}") -> if the library succeeds it would then transform the string and hand back a lowered mixin to the compiler. mixin(" this() { auto b = [ __traits(allMembers, D) ]; foreach( auto a; b) {DO.GC.Stuff...} anything... } ");
Mar 19 2012
next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
i don't get the GC relation here?

all attribute values are static const - because they are type-related 
(static) and read-only, attributes are type orientated not instance 
orientated

  GC.NoScan int value;
  GC this() {}

 Compiler Asks library for transformation of unknown  GC
 bool library(" GC this() {anything...}") ->

 if the library succeeds it would then transform the string and
 hand back a lowered mixin to the compiler.

 mixin("
 this()
 {
     auto b = [ __traits(allMembers, D) ];
     foreach( auto a; b) {DO.GC.Stuff...}

     anything...
 }
 ");
Mar 19 2012
parent reply "F i L" <witte2008 gmail.com> writes:
dennis luehring wrote:
 i don't get the GC relation here?

 all attribute values are static const - because they are 
 type-related (static) and read-only, attributes are type 
 orientated not instance orientated
classes) so the technique isn't going to be identical. In D, you'd need to be able to declare an attribute on virtually any entity declaration and have the compiler dish out the appropriate code. Trove's suggesting that the compiler simply looks up user-defined functions associated with <AttributeX> and inject the code into the appropriate place, which I think is a good idea. The problem with [FieldOffset(0)]. Things like: Int.Fast int index; assert(index.sizeof == int.sizeof) // always? which might break the type system, so I'm not sure it's even possible. The compiler might have passed the point of no-return with such attributes. Regardless, I think was Trove's suggesting would work well.
Mar 19 2012
parent dennis luehring <dl.soluz gmx.net> writes:
Am 19.03.2012 10:03, schrieb F i L:
 dennis luehring wrote:
  i don't get the GC relation here?

  all attribute values are static const - because they are
  type-related (static) and read-only, attributes are type
  orientated not instance orientated
classes) so the technique isn't going to be identical. In D, you'd need to be able to declare an attribute on virtually any entity declaration and have the compiler dish out the appropriate code. Trove's suggesting that the compiler simply looks up user-defined functions associated with<AttributeX> and inject the code into the appropriate place, which I think is a good idea. The problem with [FieldOffset(0)]. Things like: Int.Fast int index; assert(index.sizeof == int.sizeof) // always? which might break the type system, so I'm not sure it's even possible. The compiler might have passed the point of no-return with such attributes. Regardless, I think was Trove's suggesting would work well.
sound to simple to fit in non trival cases
Mar 19 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-19 09:01, Tove wrote:

 D:s CTFE... by introducing a call back from the D compiler to the
 library CTFE attribute handler... this way we can hide all reflection
 from the "end user"... and the compiler doesn't need to know anything at
 all, as the library does the semantic lowering.

  GC.NoScan int value;
  GC this() {}

 Compiler Asks library for transformation of unknown  GC
 bool library(" GC this() {anything...}") ->

 if the library succeeds it would then transform the string and hand back
 a lowered mixin to the compiler.

 mixin("
 this()
 {
 auto b = [ __traits(allMembers, D) ];
 foreach( auto a; b) {DO.GC.Stuff...}

 anything...
 }
 ");
That would be cool. -- /Jacob Carlborg
Mar 19 2012
prev sibling parent Manu <turkeyman gmail.com> writes:
On 19 March 2012 08:46, dennis luehring <dl.soluz gmx.net> wrote:

 attributes does not containing code - there just a (at-runtime) queryable
 information that can be attached to serveral things (like classes, methods,
 ...) - think of it like double.epsilon - but extendable by users - thats

 like an class (but without code)


 if something is annotated with an special attribute and use the configure
 information and do something with it - call an constructor, open an
 connection, generated code (at runtime) - whatever you want

 its a easy-to-use-buildin-**attribution-system thats it - and people like

 attributes that are then used for stuff like serialization, memory-layout,
 ...

 a compiletime example of this could be:

 attribute my_special_attribute
 {
   int version;
 }

 attribute my_special_attribute2
 {
   string test;
 }

 class test
 {
   [my_special_attribute(version=**2)]
   int method1();

   [my_special_attribute(version=**2)]
   [my_special_attribute2(test="**bert")]
   int method2();
 }

 void main()
 {
  auto b = [ __traits(allMembers, D) ];
  foreach( auto a; b )
  {
    -->attribute query magic
    -->auto c = [ __traits(attribute("my_**special_attribute", a) ];
    -->auto c = [ __traits(attribute("my_**special_attribute2", a) ];

    //now we know all methods with my_special_attribute
    //and speical_special_attribute2 and their content (version=2
    //and test="bert"

    //now think of an template or mixing that uses this
    //information for code-generation or something like that

    //thats all
  }
 }
+1 this post
Mar 19 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-19 01:41, Walter Bright wrote:
 On 3/18/2012 2:58 PM, Jacob Carlborg wrote:
 Ok, now to the user defined attribute:

  attribute class NonSerialized {} // This is what's called a "marker"
 in Java.
 It doesn't contain any information, the information is the type itself.

  attribute class SerializeAs
 {
 string value;
 }

 class Base
 {
 int x;
  SerializeAs(foo) int y;
  NonSerialized int c;
 }

  NonSerialized class Sub : Base
 {
 int z;
 }

 Base sub = new Sub;
 serialize(sub);

 In this example the user defined attribute "NonSerialized" indicates
 that the
 class shouldn't be serialized. When the serializer is serializing
 "sub" the
 compile time type will be "Base" therefore it would need to use runtime
 reflection to also serialize the fields added in the subclass "Sub".
 To check if
 the object should be serialized the serializer need to be able to
 access user
 defined attributes using runtime reflection.
I'm sorry, I find this *massively* confusing. What is foo?
It should have been: SerializeAs("foo") int y; "foo" is the name "y" will be serialized with. Just to show an example with an attribute accepting a value.
 Why are you
 serializing something marked "NonSerialized"? I really have no idea what
 is going on with this. What is the serialize() function? How does any of
 this tell you how to serialize an int? How is Base.c also a
 NonSerialized with c in a superclass of it?
This example might not have been the best one. I might not know that "Sub" is marked as "NonSerialized". An instance of "Sub" can be part of another class that is not marked as "NonSerialized". "serialize" would be the function that performs the serialization. I cannot write a whole serialization in a newsgroup message, can I :) This example also shows that "NonSerialized" can be applied to individual fields of a class. This tell how to serialize an int. It tells if a part of an object hierarchy (class, or field) should be serialized or not.
 ??????????????????????????????????

 Maybe this is why I don't much care for attributes - it's all just a fog
 to me what is happening.
I think it's quite clear what is happening. But I've might squeezed in too much in a too small example. -- /Jacob Carlborg
Mar 19 2012
prev sibling parent "RivenTheMage" <riven-mage id.ru> writes:
On Monday, 19 March 2012 at 00:41:44 UTC, Walter Bright wrote:

 Maybe this is why I don't much care for attributes - it's all 
 just a fog to me what is happening.
Maybe this will help: http://research.microsoft.com/en-us/um/people/cszypers/events/WCOP2006/rouvoy-wcop-06.pdf Attribute-Oriented Programming ( OP) is a program-level marking technique. Basically, this approach allows developers to mark program elements (e.g., classes, methods, and fields) with annotations to indicate that they maintain application specific or domain-specific concerns. For example, a developer may define a logging annotation and associate it with a method to indicate that the calls to this method should be logged, or may define a web service annotation and associate it with a class to indicate that the class should implement a Web Service. Annotations separate application’s business logic from middleware-specific or domain-specific concerns (e.g., logging and web service functions). By hiding the implementation details of those semantics from program code, annotations increase the level of programming abstraction and reduce programming complexity, resulting in simpler and more readable programs. The program elements associated with annotations are transformed to more detailed program code by a supporting generation engine. For example, a generation engine may insert logging code into the methods associated with a logging annotation. Dependencies on the underlying middleware are thus replaced by annotations, acting as weak references — i.e., references that are not mandatory for the application. This means that the evolution of the underlying middleware is taken into account by the generation engine and let the program code unchanged.
Mar 19 2012
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 18 Mar 2012 04:03:29 -0400, Walter Bright  
<newshound2 digitalmars.com> wrote:

 On 3/17/2012 10:01 PM, F i L wrote:
 Walter Bright wrote:
 My impression is it is just obfuscation around a simple lazy  
 initialization
 pattern.

 While I can see the abstraction usefulness of compile time attribute  
 metadata,
 I am having a hard time seeing what the gain is with runtime  
 attributes over
 more traditional techniques.
I'm not sure exactly what you mean by runtime attributes. Do you mean the ability to reflect upon attributes at runtime?
I mean there is modifiable-at-runtime, instance-specific data.
I don't think we should go this far with attributes. If you want to store instance-specific data, we already have a place for that. I haven't been exactly groking all of this thread, but I understand what attributes are
 Again, I am just not seeing the leap in power with this. It's a mystery  
 to me how a user defined "attribute class" is different in any way from  
 a user defined class, and what advantage special syntax gives a standard  
 pattern for lazy initialization, and why extra attribute fields can't be  
 just, well, fields.
The advantage is it hooks you into the compiler's generation of metadata and runtime type information (i.e. TypeInfo). It basically allows you to describe your type in ways that the compiler doesn't natively support. It's purely a type-description thing, I think it has nothing to do with instances. The serializable example is probably most used because it's one of the templates, the impact is somewhat greater than in D, but I believe Jacob has quite a bit of experience with serialization of types given that he wrote a serializer for D, so he would be the one I'd ask as to why it's better. I think another great example is data you can pass to the GC (if we ever get precise scanning). used objects to define what methods were run during certain test processes (the application managed the automated testing of servers remotely). The entire script was constructed based on an xml file, and the library that edited/built the script had no access or knowledge of the types it was constructing, it used pure reflection to do everything. The cool thing (in my mind at least) was, I had a script builder application which "opened" the executable to find all the scripting objects. It did not need to be recompiled, nor did it directly link with a library having the script objects, because it used reflection to determine everything I needed. Some specific attributes weren't available in reflection, so I just added some user-defined attributes, and in that way, I was able to affect the metadata to pass information to the script building application. I think also you can make code more readable and easier to write with attributes, because you can specify/convey information rather than interpreting or making assumptions, or inventing types that have nothing to do with the way the type works. For example, you could do something like: struct noSerInt { int _val; alias this _val; } struct X { noSerInt x; // clue to not serialize x } This is extra complication which is not needed while using an instance of X *except* when serializing it. I think it's cleaner to put this information into the metadata instead of having to re-implement int. Compare to: struct X { noSer int x; } Where you can safely ignore the noSer attribute for most intents and purposes. I think that the sole purpose of attributes is compile-time generation of compile and run-time query-able data. Nothing else really makes sense to me. -Steve
Mar 19 2012
next sibling parent dennis luehring <dl.soluz gmx.net> writes:
exactly my point - thx

Am 19.03.2012 13:31, schrieb Steven Schveighoffer:
 On Sun, 18 Mar 2012 04:03:29 -0400, Walter Bright
 <newshound2 digitalmars.com>  wrote:

  On 3/17/2012 10:01 PM, F i L wrote:
  Walter Bright wrote:
  My impression is it is just obfuscation around a simple lazy
  initialization
  pattern.

  While I can see the abstraction usefulness of compile time attribute
  metadata,
  I am having a hard time seeing what the gain is with runtime
  attributes over
  more traditional techniques.
I'm not sure exactly what you mean by runtime attributes. Do you mean the ability to reflect upon attributes at runtime?
I mean there is modifiable-at-runtime, instance-specific data.
I don't think we should go this far with attributes. If you want to store instance-specific data, we already have a place for that. I haven't been exactly groking all of this thread, but I understand what attributes are
  Again, I am just not seeing the leap in power with this. It's a mystery
  to me how a user defined "attribute class" is different in any way from
  a user defined class, and what advantage special syntax gives a standard
  pattern for lazy initialization, and why extra attribute fields can't be
  just, well, fields.
The advantage is it hooks you into the compiler's generation of metadata and runtime type information (i.e. TypeInfo). It basically allows you to describe your type in ways that the compiler doesn't natively support. It's purely a type-description thing, I think it has nothing to do with instances. The serializable example is probably most used because it's one of the templates, the impact is somewhat greater than in D, but I believe Jacob has quite a bit of experience with serialization of types given that he wrote a serializer for D, so he would be the one I'd ask as to why it's better. I think another great example is data you can pass to the GC (if we ever get precise scanning). used objects to define what methods were run during certain test processes (the application managed the automated testing of servers remotely). The entire script was constructed based on an xml file, and the library that edited/built the script had no access or knowledge of the types it was constructing, it used pure reflection to do everything. The cool thing (in my mind at least) was, I had a script builder application which "opened" the executable to find all the scripting objects. It did not need to be recompiled, nor did it directly link with a library having the script objects, because it used reflection to determine everything I needed. Some specific attributes weren't available in reflection, so I just added some user-defined attributes, and in that way, I was able to affect the metadata to pass information to the script building application. I think also you can make code more readable and easier to write with attributes, because you can specify/convey information rather than interpreting or making assumptions, or inventing types that have nothing to do with the way the type works. For example, you could do something like: struct noSerInt { int _val; alias this _val; } struct X { noSerInt x; // clue to not serialize x } This is extra complication which is not needed while using an instance of X *except* when serializing it. I think it's cleaner to put this information into the metadata instead of having to re-implement int. Compare to: struct X { noSer int x; } Where you can safely ignore the noSer attribute for most intents and purposes. I think that the sole purpose of attributes is compile-time generation of compile and run-time query-able data. Nothing else really makes sense to me. -Steve
Mar 19 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-19 13:31, Steven Schveighoffer wrote:
 On Sun, 18 Mar 2012 04:03:29 -0400, Walter Bright
 I mean there is modifiable-at-runtime, instance-specific data.
I don't think we should go this far with attributes. If you want to store instance-specific data, we already have a place for that. I haven't been exactly groking all of this thread, but I understand what
I agree.
 Again, I am just not seeing the leap in power with this. It's a
 mystery to me how a user defined "attribute class" is different in any
 way from a user defined class, and what advantage special syntax gives
 a standard pattern for lazy initialization, and why extra attribute
 fields can't be just, well, fields.
The advantage is it hooks you into the compiler's generation of metadata and runtime type information (i.e. TypeInfo). It basically allows you to describe your type in ways that the compiler doesn't natively support. It's purely a type-description thing, I think it has nothing to do with instances. The serializable example is probably most used because it's one of the templates, the impact is somewhat greater than in D, but I believe Jacob has quite a bit of experience with serialization of types given that he wrote a serializer for D, so he would be the one I'd ask as to why it's better. I think another great example is data you can pass to the GC (if we ever get precise scanning).
For the serialization example I think it's mostly a syntactic enhancement. It would also be an enhancement when accessing the attributes. class Foo { int a; NonSerialized int b; } class Bar { int a; int b; mixin NonSerialized!(b); } The advantage the attribute has is: * Better syntax, more readable * Shows up in the documentation of "b". * Can be repeated on several fields (with the mixin you can only mixin "NonSerialized" once) * Easier to access the attribute * No information is added to the type itself. It would be added to the metadata of the type, i.e. TypeInfo But I see many other cases where attributes would work better than mixins.

 used objects to define what methods were run during certain test
 processes (the application managed the automated testing of servers
 remotely). The entire script was constructed based on an xml file, and
 the library that edited/built the script had no access or knowledge of
 the types it was constructing, it used pure reflection to do everything.
 The cool thing (in my mind at least) was, I had a script builder
 application which "opened" the executable to find all the scripting
 objects. It did not need to be recompiled, nor did it directly link with
 a library having the script objects, because it used reflection to
 determine everything I needed. Some specific attributes weren't
 available in reflection, so I just added some user-defined attributes,
 and in that way, I was able to affect the metadata to pass information
 to the script building application.

 I think also you can make code more readable and easier to write with
 attributes, because you can specify/convey information rather than
 interpreting or making assumptions, or inventing types that have nothing
 to do with the way the type works.

 For example, you could do something like:

 struct noSerInt
 {
 int _val;
 alias this _val;
 }

 struct X
 {
 noSerInt x; // clue to not serialize x
 }

 This is extra complication which is not needed while using an instance
 of X *except* when serializing it. I think it's cleaner to put this
 information into the metadata instead of having to re-implement int.
 Compare to:

 struct X
 {
  noSer int x;
 }

 Where you can safely ignore the  noSer attribute for most intents and
 purposes.

 I think that the sole purpose of attributes is compile-time generation
 of compile and run-time query-able data. Nothing else really makes sense
 to me.

 -Steve
Very well explained. -- /Jacob Carlborg
Mar 19 2012
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/19/12, Jacob Carlborg <doob me.com> wrote:
 * Can be repeated on several fields (with the mixin you can only mixin
 "NonSerialized" once)
When I implemented NonSerialized for Vladimir's json library he made a suggestion to simply create enums of each field that is not to be serialized and encode it as "fieldname_nonSerialized". That would enable using a NonSerialized mixin multiple times. I've yet to implement it in that way, I ran into some odd bugs but I'll have a look at this soon. My implementation used a hash lookup table for the fields, but using enums would make the code even simpler. Basically: struct Foo { int x; string name; mixin(NonSerialized!name); string lastName; mixin(NonSerialized!lastName); } and this would expand to: struct Foo { int x; string name; enum name_nonSerialized; string lastName; enum lastName_nonSerialized; } So all you'd have to do is use compile-time introspection and a little bit of string processing to figure out if a field should be serialized or not.
Mar 19 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
On Monday, 19 March 2012 at 20:44:43 UTC, Andrej Mitrovic wrote:
 On 3/19/12, Jacob Carlborg <doob me.com> wrote:
 * Can be repeated on several fields (with the mixin you can 
 only mixin
 "NonSerialized" once)
When I implemented NonSerialized for Vladimir's json library he made a suggestion to simply create enums of each field that is not to be serialized and encode it as "fieldname_nonSerialized". That would enable using a NonSerialized mixin multiple times. I've yet to implement it in that way, I ran into some odd bugs but I'll have a look at this soon. My implementation used a hash lookup table for the fields, but using enums would make the code even simpler. Basically: struct Foo { int x; string name; mixin(NonSerialized!name); string lastName; mixin(NonSerialized!lastName); } and this would expand to: struct Foo { int x; string name; enum name_nonSerialized; string lastName; enum lastName_nonSerialized; } So all you'd have to do is use compile-time introspection and a little bit of string processing to figure out if a field should be serialized or not.
I think this could get tricky for the compiler to confidently use given that the mixed in enums can collide with existing members (although not likely). Also, if objects are not identified as being unique marked (in the compilers eyes), what specific attribute post-fixes will it be searching for on enums members?
Mar 19 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/19/12 3:55 PM, F i L wrote:
 I think this could get tricky for the compiler to confidently use given
 that the mixed in enums can collide with existing members (although not
 likely). Also, if objects are not identified as being unique marked (in
 the compilers eyes), what specific attribute post-fixes will it be
 searching for on enums members?
Not a big issue, the name could always contain a uuid which makes collision practically impossibile. Andrei
Mar 19 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu 
wrote:
 On 3/19/12 3:55 PM, F i L wrote:
 I think this could get tricky for the compiler to confidently 
 use given
 that the mixed in enums can collide with existing members 
 (although not
 likely). Also, if objects are not identified as being unique 
 marked (in
 the compilers eyes), what specific attribute post-fixes will 
 it be
 searching for on enums members?
Not a big issue, the name could always contain a uuid which makes collision practically impossibile. Andrei
So the compiler would always search for enums with a specific "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" signature? Guess that could work.
Mar 19 2012
prev sibling parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu 
wrote:
 On 3/19/12 3:55 PM, F i L wrote:
 I think this could get tricky for the compiler to confidently 
 use given
 that the mixed in enums can collide with existing members 
 (although not
 likely). Also, if objects are not identified as being unique 
 marked (in
 the compilers eyes), what specific attribute post-fixes will 
 it be
 searching for on enums members?
Not a big issue, the name could always contain a uuid which makes collision practically impossibile. Andrei
And with parameters? Or how about run-time reflection, which is something that should be added eventually for attributes as well (that is, typeid(Blah).getCustomAttributes())? We can't manually add things into the TypeInfo. This is an already ridiculously hackish approach, and it *does not* work for anything besides trivial applications. It is completly unreasonable to expect everything to be done in a library; the compiler exists for a reason and at some point you have to draw the line. Why even bother having an auto keyword? Instead of 'auto a = b + c' you could simply use 'Auto!(b + c) a = b + c'. It is a more extreme example for now, but won't be too much different once more complicated things arise; after all, auto!2 a = 2 is not much different from auto a = 2. Just like it's not too much different for parameterless attributes (even if it is uglier and much more hackish), but once you get into parameters and runtime stuff it becomes just as ugly as the above example.
Mar 19 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 12:50 AM, Kapps wrote:
 On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:
 On 3/19/12 3:55 PM, F i L wrote:
 I think this could get tricky for the compiler to confidently use given
 that the mixed in enums can collide with existing members (although not
 likely). Also, if objects are not identified as being unique marked (in
 the compilers eyes), what specific attribute post-fixes will it be
 searching for on enums members?
Not a big issue, the name could always contain a uuid which makes collision practically impossibile. Andrei
And with parameters? Or how about run-time reflection, which is something that should be added eventually for attributes as well (that is, typeid(Blah).getCustomAttributes())? We can't manually add things into the TypeInfo.
Perhaps we should add a field of type Variant[string].
 This is an already ridiculously hackish approach, and
 it *does not* work for anything besides trivial applications. It is
 completly unreasonable to expect everything to be done in a library; the
 compiler exists for a reason and at some point you have to draw the
 line.
I'm afraid I disagree. A targeted language feature definitely makes a library approach inferior, by definition. But adding features is cheating, like printing money is for a government: very tempting, with apparently good short-term benefits, but with devastating cumulative effects. Also, as I mentioned, the availability of the easy escape hatch of adding a language feature thwarts creativity. Nobody will care to think about and come with idioms that use the language to do great things, if they know a language feature could be always added that makes things "nicer". I'm not saying this particular feature should or should not be in the language, but I wish our community exercised considerably more restraint when it comes about adding new language features.
 Why even bother having an auto keyword? Instead of 'auto a = b +
 c' you could simply use 'Auto!(b + c) a = b + c'. It is a more extreme
 example for now, but won't be too much different once more complicated
 things arise; after all, auto!2 a = 2 is not much different from auto a
 = 2. Just like it's not too much different for parameterless attributes
 (even if it is uglier and much more hackish), but once you get into
 parameters and runtime stuff it becomes just as ugly as the above example.
I don't think your example with auto is relevant. Andrei
Mar 20 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
 This is an already ridiculously hackish approach, and
 it *does not* work for anything besides trivial applications. It is
 completly unreasonable to expect everything to be done in a library; the
 compiler exists for a reason and at some point you have to draw the
 line.
I'm afraid I disagree. A targeted language feature definitely makes a library approach inferior, by definition. But adding features is cheating, like printing money is for a government: very tempting, with apparently good short-term benefits, but with devastating cumulative effects. Also, as I mentioned, the availability of the easy escape hatch of adding a language feature thwarts creativity. Nobody will care to think about and come with idioms that use the language to do great things, if they know a language feature could be always added that makes things "nicer". I'm not saying this particular feature should or should not be in the language, but I wish our community exercised considerably more restraint when it comes about adding new language features.
See my reply to one of your other posts: http://forum.dlang.org/thread/bccwycoexxykfgxvedix forum.dlang.org?page=9#post-jk9gk8:242t7k:241:40digitalmars.com -- /Jacob Carlborg
Mar 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
 I'm not saying this particular feature should or should not be in the
 language, but I wish our community exercised considerably more restraint
 when it comes about adding new language features.
See my reply to one of your other posts: http://forum.dlang.org/thread/bccwycoexxykfgxvedix forum.dlang.org?page=9#post-jk9gk8:242t7k:241:40digitalmars.com
I understand the intention there, but my point stands: let's first explore doing it in the language, and advocate a new feature only when that fails. We've been too trigger-happy about proposing features even for the most trivial inconveniences. Andrei
Mar 20 2012
next sibling parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
Andrei Alexandrescu wrote:
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
Please, do not transform D into a dynamic language.
Mar 20 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 2:04 PM, Piotr Szturmaj wrote:
 Andrei Alexandrescu wrote:
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
Please, do not transform D into a dynamic language.
Where's the "dynamic languages rok" crowd when you need it :o). Andrei
Mar 20 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/03/2012 20:17, Andrei Alexandrescu a écrit :
 On 3/20/12 2:04 PM, Piotr Szturmaj wrote:
 Andrei Alexandrescu wrote:
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
Please, do not transform D into a dynamic language.
Where's the "dynamic languages rok" crowd when you need it :o). Andrei
They are busy debugging what they coded sooooooo quickly and efficiently.
Mar 20 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-20 20:17, Andrei Alexandrescu wrote:
 On 3/20/12 2:04 PM, Piotr Szturmaj wrote:
 Please, do not transform D into a dynamic language.
Where's the "dynamic languages rok" crowd when you need it :o). Andrei
They left you behind after all those "d rox" comments :) -- /Jacob Carlborg
Mar 20 2012
prev sibling next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/03/2012 17:13, Andrei Alexandrescu a écrit :
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
I was probably one of the most controversial part of the thread :D
Mar 20 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-20 17:13, Andrei Alexandrescu wrote:
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
Maybe you agreed on that. I still don't like it. -- /Jacob Carlborg
Mar 20 2012
parent Don Clugston <dac nospam.com> writes:
On 20/03/12 22:29, Jacob Carlborg wrote:
 On 2012-03-20 17:13, Andrei Alexandrescu wrote:
 On 3/20/12 10:52 AM, Jacob Carlborg wrote:
 On 2012-03-20 16:17, Andrei Alexandrescu wrote:
 On 3/20/12 12:50 AM, Kapps wrote:
 Perhaps we should add a field of type Variant[string].
No, not Variant[string] again.
Why? I thought it was agreed that that's a good idea for exceptions.
Maybe you agreed on that. I still don't like it.
I'm not sure who agreed, but I don't even think it would work. What happens if the AA or Variant throws an exception? Especially if AAs become a library type, they've got no business being in something as fundamental as an exception base class. It leads to a big ball of mud design.
Mar 22 2012
prev sibling next sibling parent "F i L" <witte2008 gmail.com> writes:
Andrei Alexandrescu wrote:
 like printing money is for a government: very tempting, with 
 apparently good short-term benefits, but with devastating 
 cumulative effects.
RON PAUL 2012!!! sorry, I couldn't help myself.
 Also, as I mentioned, the availability of the easy escape hatch 
 of adding a language feature thwarts creativity. Nobody will 
 care to think about and come with idioms that use the language 
 to do great things, if they know a language feature could be 
 always added that makes things "nicer".
You can be creative with a sledge hammer, or you can be creative with a jack hammer. D is already a bulldozer, but that doesn't mean it shouldn't become a wreaking ball. ;-)
Mar 20 2012
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 20 Mar 2012 11:17:02 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Perhaps we should add a field of type Variant[string].
1. Variant is not (and shouldn't be IMO) part of druntime. 2. This is *static* data that should be available at compile time. D's runtime wasting cycles filling in static data (including lots of heap allocations) seems like a big enough detriment that compiler help is warranted.
 This is an already ridiculously hackish approach, and
 it *does not* work for anything besides trivial applications. It is
 completly unreasonable to expect everything to be done in a library; the
 compiler exists for a reason and at some point you have to draw the
 line.
I'm afraid I disagree. A targeted language feature definitely makes a library approach inferior, by definition. But adding features is cheating, like printing money is for a government: very tempting, with apparently good short-term benefits, but with devastating cumulative effects.
Not exactly. We could all be writing code in assembly if "adding features" was always caustic. What is nice about adding this feature is it provides a custom solution to hooking the compiler's TypeInfo generation. We are setting up the compiler to give us hooks so we *don't* have to extend the compiler every time we want new reflection features. One of the oft-requested features of D is reflection capability. The answer is generally that we have compile-time reflection, which can theoretically be used to build runtime reflection. However, the rebuttal I always come back with is "yeah, but why should I build at runtime that which is obvious at compile time?" Note that one library that did attempt runtime reflection capability (flectioned) does all this at runtime, and does some really funky shit, like opening /proc/self/map on Linux, or requiring you to pass an OPTLINK map file. I don't look at these as "innovations" as much as I do as workarounds.
 Also, as I mentioned, the availability of the easy escape hatch of  
 adding a language feature thwarts creativity. Nobody will care to think  
 about and come with idioms that use the language to do great things, if  
 they know a language feature could be always added that makes things  
 "nicer".
I have to disagree for this instance, the barrier to creating reflection data from compile-time info is very large. If anything, this *expands* the ability to create, since you now have a new way to pass information from compiler to runtime. -Steve
Mar 20 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 11:09 AM, Steven Schveighoffer wrote:
 On Tue, 20 Mar 2012 11:17:02 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 I'm afraid I disagree. A targeted language feature definitely makes a
 library approach inferior, by definition. But adding features is
 cheating, like printing money is for a government: very tempting, with
 apparently good short-term benefits, but with devastating cumulative
 effects.
Not exactly. We could all be writing code in assembly if "adding features" was always caustic.
This is trivializing my point. Please.
 What is nice about adding this feature is it provides a custom solution
 to hooking the compiler's TypeInfo generation. We are setting up the
 compiler to give us hooks so we *don't* have to extend the compiler
 every time we want new reflection features.
There is something nice about every new feature.
 One of the oft-requested features of D is reflection capability. The
 answer is generally that we have compile-time reflection, which can
 theoretically be used to build runtime reflection. However, the rebuttal
 I always come back with is "yeah, but why should I build at runtime that
 which is obvious at compile time?"
I thought there were discussions that didn't add runtime overhead.
 Note that one library that did attempt runtime reflection capability
 (flectioned) does all this at runtime, and does some really funky shit,
 like opening /proc/self/map on Linux, or requiring you to pass an
 OPTLINK map file. I don't look at these as "innovations" as much as I do
 as workarounds.
Maybe there's a better approach than flectioned. Consider the language is frozen solid. How would you solve problems with it?
 Also, as I mentioned, the availability of the easy escape hatch of
 adding a language feature thwarts creativity. Nobody will care to
 think about and come with idioms that use the language to do great
 things, if they know a language feature could be always added that
 makes things "nicer".
I have to disagree for this instance, the barrier to creating reflection data from compile-time info is very large. If anything, this *expands* the ability to create, since you now have a new way to pass information from compiler to runtime.
I hope I'll be convinced. Andrei
Mar 20 2012
next sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/03/2012 17:16, Andrei Alexandrescu a écrit :
 Maybe there's a better approach than flectioned. Consider the language
 is frozen solid. How would you solve problems with it?
Easy, I already did that many times. I'd preprocess the source code with some homebrew tool before compiling.
Mar 20 2012
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 20 Mar 2012 12:16:41 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 3/20/12 11:09 AM, Steven Schveighoffer wrote:
 On Tue, 20 Mar 2012 11:17:02 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:
 I'm afraid I disagree. A targeted language feature definitely makes a
 library approach inferior, by definition. But adding features is
 cheating, like printing money is for a government: very tempting, with
 apparently good short-term benefits, but with devastating cumulative
 effects.
Not exactly. We could all be writing code in assembly if "adding features" was always caustic.
This is trivializing my point. Please.
Well, then let's not compare the hyperinflation of the economy to adding a compiler feature. There are certainly good reasons to add compiler features, without devastating cumulative effects.
 What is nice about adding this feature is it provides a custom solution
 to hooking the compiler's TypeInfo generation. We are setting up the
 compiler to give us hooks so we *don't* have to extend the compiler
 every time we want new reflection features.
There is something nice about every new feature.
Yes, but this nicety is not just syntax sugar or a shortcut, it's adding a whole new opportunity of communication through the compile-time/runtime barrier. I think Adam Ruppe's post on how he tried to use library code to accomplish this is quite telling. The comparison to OO programming in C vs. C++/D seems quite appropriate. And I've written Xt widgets, so I can vouch for how shitty it is to do OO programming in C.
 One of the oft-requested features of D is reflection capability. The
 answer is generally that we have compile-time reflection, which can
 theoretically be used to build runtime reflection. However, the rebuttal
 I always come back with is "yeah, but why should I build at runtime that
 which is obvious at compile time?"
I thought there were discussions that didn't add runtime overhead.
I haven't seen that. Perhaps a link?
 Note that one library that did attempt runtime reflection capability
 (flectioned) does all this at runtime, and does some really funky shit,
 like opening /proc/self/map on Linux, or requiring you to pass an
 OPTLINK map file. I don't look at these as "innovations" as much as I do
 as workarounds.
Maybe there's a better approach than flectioned. Consider the language is frozen solid. How would you solve problems with it?
I think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -Steve
Mar 21 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 14:54, Steven Schveighoffer wrote:
 On Tue, 20 Mar 2012 12:16:41 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Maybe there's a better approach than flectioned. Consider the language
 is frozen solid. How would you solve problems with it?
I think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -Steve
With Orange I'm doing everything with compile time reflection. Flectioned was all about runtime reflection. It's possible to replace methods with flectioned at runtime and do a lot of crazy things. In Orange I'm using mixins, tupleof and stringof to accomplish most of the compile time reflection. Example: class Foo { int a; int b; mixin NonSerialized!(b); } serialize(new Foo); "mixin NonSerialized!(b);" is expanded to: static const __nonSerialized = ["a"]; The fields are enumerated and serialized using tupleof. It also checks all structs and classes if they have "__nonSerialized" defined. -- /Jacob Carlborg
Mar 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 9:57 AM, Jacob Carlborg wrote:
 On 2012-03-21 14:54, Steven Schveighoffer wrote:
 On Tue, 20 Mar 2012 12:16:41 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Maybe there's a better approach than flectioned. Consider the language
 is frozen solid. How would you solve problems with it?
I think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -Steve
With Orange I'm doing everything with compile time reflection. Flectioned was all about runtime reflection. It's possible to replace methods with flectioned at runtime and do a lot of crazy things. In Orange I'm using mixins, tupleof and stringof to accomplish most of the compile time reflection. Example: class Foo { int a; int b; mixin NonSerialized!(b); }
I think the liability here is that b needs to appear in two places, once in the declaration proper and then in the NonSerialized part. (A possible advantage is that sometimes it may be advantageous to keep all symbols with a specific attribute in one place.) A possibility would be to make the mixin expand to the field and the metadata at once.
 serialize(new Foo);

 "mixin NonSerialized!(b);" is expanded to:

 static const __nonSerialized = ["a"];

 The fields are enumerated and serialized using tupleof. It also checks
 all structs and classes if they have "__nonSerialized" defined.
Did you mean static const __nonSerialized = ["b"]; ? In case there are several non-serialized variables, how do you avoid clashes between different definitions of __nonSerialized? Thanks, Andrei
Mar 21 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
Andrei Alexandrescu wrote:
 In case there are several non-serialized variables, how do you 
 avoid clashes between different definitions of __nonSerialized?
struct A { int a, b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["a", "b"];
Mar 21 2012
next sibling parent reply "F i L" <witte2008 gmail.com> writes:
On Wednesday, 21 March 2012 at 15:20:35 UTC, F i L wrote:
 Andrei Alexandrescu wrote:
 In case there are several non-serialized variables, how do you 
 avoid clashes between different definitions of __nonSerialized?
struct A { int a, b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["a", "b"];
Also, if where meant how could you store multiple types, you could just use an Associative Array: struct A { int a; float b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["int":"a", "int":"b"];
Mar 21 2012
next sibling parent "F i L" <witte2008 gmail.com> writes:
 static const __nonSerialized = ["int":"a", "int":"b"];
Typo, I meant: ["int":"a", "float":"b"];
Mar 21 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 10:26 AM, F i L wrote:
 On Wednesday, 21 March 2012 at 15:20:35 UTC, F i L wrote:
 Andrei Alexandrescu wrote:
 In case there are several non-serialized variables, how do you avoid
 clashes between different definitions of __nonSerialized?
struct A { int a, b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["a", "b"];
Also, if where meant how could you store multiple types, you could just use an Associative Array: struct A { int a; float b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["int":"a", "int":"b"];
Is this implemented, or a thought? Andrei
Mar 21 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Andrei Alexandrescu wrote:
 Is this implemented, or a thought?
Just a thought (I even accidentally reversed the key-value order), but I don't see why it couldn't work. I'll try and make up a quick implementation.
Mar 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 11:15 AM, F i L wrote:
 Andrei Alexandrescu wrote:
 Is this implemented, or a thought?
Just a thought (I even accidentally reversed the key-value order), but I don't see why it couldn't work. I'll try and make up a quick implementation.
Wouldn't a better approach rely on a compile-time structure instead of a hash? Andrei
Mar 21 2012
parent reply "F i L" <witte2008 gmail.com> writes:
On Wednesday, 21 March 2012 at 16:21:21 UTC, Andrei Alexandrescu 
wrote:
 On 3/21/12 11:15 AM, F i L wrote:
 Andrei Alexandrescu wrote:
 Is this implemented, or a thought?
Just a thought (I even accidentally reversed the key-value order), but I don't see why it couldn't work. I'll try and make up a quick implementation.
Wouldn't a better approach rely on a compile-time structure instead of a hash?
I'm not sure, you're understand of D's compiler-time structures is much better than mine. Serialization "attributes" are mostly used at runtime, so having mixing in a static enum *should* mean reflection upon the property at both runtime and compiletime.
Mar 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-21 17:27, F i L wrote:

 I'm not sure, you're understand of D's compiler-time structures is much
 better than mine. Serialization "attributes" are mostly used at runtime,
 so having mixing in a static enum *should* mean reflection upon the
 property at both runtime and compiletime.
I'm using the serialization attributes at compile time. -- /Jacob Carlborg
Mar 21 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 16:26, F i L wrote:

 Also, if where meant how could you store multiple types, you could just
 use an Associative Array:

 struct A {
 int a;
 float b;
 mixin NonSerialized!(a, b);
 }

 static const __nonSerialized = ["int":"a", "int":"b"];
Only the name of the variables are necessary in my serialization library. -- /Jacob Carlborg
Mar 21 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Jacob Carlborg wrote:
 Only the name of the variables are necessary in my 
 serialization library.
Wouldn't mixing in an enum be more useful? You could use it at compile time that way.
Mar 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-21 18:22, F i L wrote:
 Jacob Carlborg wrote:
 Only the name of the variables are necessary in my serialization library.
Wouldn't mixing in an enum be more useful? You could use it at compile time that way.
I'm using a static const variable because it's compatible with D1. It still works at compile time. -- /Jacob Carlborg
Mar 21 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 10:20 AM, F i L wrote:
 Andrei Alexandrescu wrote:
 In case there are several non-serialized variables, how do you avoid
 clashes between different definitions of __nonSerialized?
struct A { int a, b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["a", "b"];
That's may be a problem because it makes it impossible to put together fields and their non-serializable property. Ideally the above should work, and also mixing in several NonSerialized instances within the same type should also work. Andrei
Mar 21 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 16:02:22 UTC, Andrei Alexandrescu 
wrote:
 Ideally the above should work, and also mixing in several 
 NonSerialized instances within the same type should also work.
Oh god, I feel like the spawn of Hacktan. I'm taking the DSL identifiers to the max. Consider the following: http://arsdnet.net/dcode/omg.d Bottom line: struct A { int a; int b; mixin Note!(a, q{ "NonSerialized" }); mixin Note!(a, q{ "Awesome" }); mixin Note!(b, q{ "Filthy Hack" }); } void main() { pragma(msg, getNotes!(A.a)); } $ dmd omg.d ["NotSerialized", "Awesome"] How does it work? What it does is for each note, it creates a dummy enum. The enum's name is a D expression, encoded as a valid identifier. This expression is a ~= op here. The struct looks like this: struct A{ int a; int b; enum _attr_mixin_a_32_4c_NonSerialized; enum _attr_mixin_a_32_4c_Awesome; enum _attr_mixin_b_32_4c_Filthy_32_Hack; } getNotes looks for this pattern, decodes it, and then mixes in the expression: string[] notes; if(identifier is the right item) mixin("notes " ~ decoded_identifier); return notes; So, it embeds D code to rebuild the requested data as the names of identifiers. We run it every time we want to get it. Note that everything I said in my long post still applies here. Like I said before, we *can* make it work, but it comes with a lot of baggage that hurts maintainability and interop with third party code. Remember, you have to add code to your library just to *ignore* these attributes. You can't just ignore them, no, you have to add special code to skip them. That's no good. This also has duplicated names and the other problems mentioned before with overloading, inheritance and more. The compiler keeping a list of notes attached to the declaration remains the only *right* way to do it. That it is slightly prettier is an added benefit, but not the main reason why we need it there.
Mar 21 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 17:26:19 UTC, Adam D. Ruppe wrote:
 The compiler keeping a list of notes attached to the
 declaration remains the only *right* way to do it.
Another note on correctness btw, this thing is wrong if in other modules. mixin Note!(a, q{ NotSerialized() }); that string loses the exact type. But, let's say we solved that, and uses the module name or something. But then, what if: module cool; import serialization.attributes; struct cool { int a; mixin Note!(a, serialization.attributes.amazing); } == // another module import cool; void main() { getNotes!(cool.cool.a); } That won't compile. When it tries to mixin the note, it will say undefinied identifier "amazing" because the mixin is evaluated in another scope than the original declaration. So while module cool imported serilization.attributes, module main or module notes (I'm not sure exactly where this is mixed in by the time we're all said and done tbh but it doesn't matter) has not. Thus the mixin will fail. I had this same problem when trying to do default arguments in web.d by parsing stringof and mixing it in. It works for built-in types like string and int, but it doesn't work for user defined types, which are perfectly valid function params (they work fine in the rest of web.d). But the mixin scope is wrong so those types become undefined. Again, we can get kinda close with these D tricks, but it just cannot go all the way - and much of the interesting stuff is behind that barrier. The compiler can do it easily though, and get it right, enabling us to do a lot more in the library down the line.
Mar 21 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-21 16:20, F i L wrote:
 Andrei Alexandrescu wrote:
 In case there are several non-serialized variables, how do you avoid
 clashes between different definitions of __nonSerialized?
struct A { int a, b; mixin NonSerialized!(a, b); } static const __nonSerialized = ["a", "b"];
Exactly. -- /Jacob Carlborg
Mar 21 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 16:11, Andrei Alexandrescu wrote:
I think the liability here is that b needs to appear in two places, once
 in the declaration proper and then in the NonSerialized part. (A
 possible advantage is that sometimes it may be advantageous to keep all
 symbols with a specific attribute in one place.) A possibility would be
 to make the mixin expand to the field and the metadata at once.
Yes, but that just looks ugly: class Foo { int a; mixin NonSerialized!(int, "b"); } That's why it's so nice with attributes.
 Did you mean

 static const __nonSerialized = ["b"];

 ?
Yes, sorry.
 In case there are several non-serialized variables, how do you avoid
 clashes between different definitions of __nonSerialized?
In my current implementation you are forced to only mixin one NonSerialized per type: class Foo { int a; int b; mixin NonSerialized!(a, b); } Of course there are ways around that with various pros and cons. -- /Jacob Carlborg
Mar 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 12:06 PM, Jacob Carlborg wrote:
 On 2012-03-21 16:11, Andrei Alexandrescu wrote:
 I think the liability here is that b needs to appear in two places, once
 in the declaration proper and then in the NonSerialized part. (A
 possible advantage is that sometimes it may be advantageous to keep all
 symbols with a specific attribute in one place.) A possibility would be
 to make the mixin expand to the field and the metadata at once.
Yes, but that just looks ugly: class Foo { int a; mixin NonSerialized!(int, "b"); } That's why it's so nice with attributes.
Well if the argument boils down to nice vs. ugly, as opposed to possible vs. impossible - it's quite a bit less compelling. Andrei
Mar 21 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-21 19:32, Andrei Alexandrescu wrote:

 Well if the argument boils down to nice vs. ugly, as opposed to possible
 vs. impossible - it's quite a bit less compelling.
No, that's just one part on the problem. Read Adam D. Ruppe's posts in this thread, for example. -- /Jacob Carlborg
Mar 21 2012
prev sibling next sibling parent reply Manu <turkeyman gmail.com> writes:
On 21 March 2012 20:32, Andrei Alexandrescu
<SeeWebsiteForEmail erdani.org>wrote:

 Well if the argument boils down to nice vs. ugly, as opposed to possible
 vs. impossible - it's quite a bit less compelling.
By this logic, I might as well stick with C++. It's 'possible' to do everything I need, but it makes my cry myself to sleep at night, and wastes insane amounts of time. Code simplicity and cleanliess on the front end IS important, it makes code reasable, maintainable. In a large code team, if someone has to go out of their way to understand some code, chances are, they won't understand it, and make improper or damaging changes to it. Or make improper implementations based on it. This will destroy your code across 5 years or so. In gamedev, the average engine codebase lasts 10+ years.
Mar 22 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-22 08:17, Manu wrote:

 By this logic, I might as well stick with C++. It's 'possible' to do
 everything I need, but it makes my cry myself to sleep at night, and
 wastes insane amounts of time.
 Code simplicity and cleanliess on the front end IS important, it makes
 code reasable, maintainable.
 In a large code team, if someone has to go out of their way to
 understand some code, chances are, they won't understand it, and make
 improper or damaging changes to it. Or make improper implementations
 based on it.
 This will destroy your code across 5 years or so. In gamedev, the
 average engine codebase lasts 10+ years.
Yes, I completely agree. -- /Jacob Carlborg
Mar 25 2012
prev sibling parent Ary Manzana <ary esperanto.org.ar> writes:
On 3/22/12 2:32 AM, Andrei Alexandrescu wrote:
 On 3/21/12 12:06 PM, Jacob Carlborg wrote:
 On 2012-03-21 16:11, Andrei Alexandrescu wrote:
 I think the liability here is that b needs to appear in two places, once
 in the declaration proper and then in the NonSerialized part. (A
 possible advantage is that sometimes it may be advantageous to keep all
 symbols with a specific attribute in one place.) A possibility would be
 to make the mixin expand to the field and the metadata at once.
Yes, but that just looks ugly: class Foo { int a; mixin NonSerialized!(int, "b"); } That's why it's so nice with attributes.
Well if the argument boils down to nice vs. ugly, as opposed to possible vs. impossible - it's quite a bit less compelling. Andrei
Why don't you program everything with gotos instead of for, foreach and while? If it boils down to nice vs. ugly, as opposed to possible vs. impossible... Hmm...
Mar 22 2012
prev sibling next sibling parent reply "Tove" <tove fransson.se> writes:
On Wednesday, 21 March 2012 at 15:11:47 UTC, Andrei Alexandrescu 
wrote:
 class Foo
 {
 int a;
 int b;

 mixin NonSerialized!(b);
 }
I think the liability here is that b needs to appear in two places, once in the declaration proper and then in the NonSerialized part. (A possible advantage is that sometimes it may be advantageous to keep all symbols with a specific attribute in one place.) A possibility would be to make the mixin expand to the field and the metadata at once.
In case my proof of concept which was posted in another thread was overlooked... it was my goal to address this very issue... also it's possible to change the datatype of the members in the "parallel annotation class"(Foo_Serializable in my limited example), and store any extra user data there if so desired while keeping the real class clean... and then simply traverse with __traits. import std.stdio; import std.array; import std.string; import std.algorithm; string attr(string complex_decl) { string org_struct; string ser_struct; auto lines = splitLines(complex_decl); { auto decl = split(stripLeft(lines[0])); if(decl[0]=="struct") { org_struct = decl[0] ~ " " ~ decl[1]; ser_struct = decl[0] ~ " " ~ decl[1] ~ "_Serializable"; } else return complex_decl; } foreach(line; lines[1..$]) { auto attr = findSplitAfter(stripLeft(line), " NonSerialized "); if(attr[0]==" NonSerialized ") org_struct ~= attr[1]; else { org_struct ~= attr[1]; ser_struct ~= attr[1]; } } return ser_struct ~ "\n" ~ org_struct; } mixin(attr(q{struct Foo { NonSerialized int x; NonSerialized int y; int z; }})); void main() { auto m = [ __traits(allMembers, Foo) ]; writeln("Normal members of Foo:", m); auto n = [ __traits(allMembers, Foo_Serializable) ]; writeln("Serializable members of Foo:", n); }
Mar 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-22 02:23, Tove wrote:

 mixin(attr(q{struct Foo
 {
  NonSerialized int x;
  NonSerialized int y;
 int z;
 }}));

 void main()
 {
 auto m = [ __traits(allMembers, Foo) ];
 writeln("Normal members of Foo:", m);

 auto n = [ __traits(allMembers, Foo_Serializable) ];
 writeln("Serializable members of Foo:", n);
 }
Just really ugly and it creates a new type, completely unnecessary if D supported user defined attributes. -- /Jacob Carlborg
Mar 25 2012
parent "Tove" <tove fransson.se> writes:
On Sunday, 25 March 2012 at 15:24:18 UTC, Jacob Carlborg wrote:
 On 2012-03-22 02:23, Tove wrote:

 mixin(attr(q{struct Foo
 {
  NonSerialized int x;
  NonSerialized int y;
 int z;
 }}));

 void main()
 {
 auto m = [ __traits(allMembers, Foo) ];
 writeln("Normal members of Foo:", m);

 auto n = [ __traits(allMembers, Foo_Serializable) ];
 writeln("Serializable members of Foo:", n);
 }
Just really ugly and it creates a new type, completely unnecessary if D supported user defined attributes.
Well... "eye of the beholder"... I think that's exactly the beautiful part, because: 1) The original type is 100% unaltered... 2) Easy for the compiler to optimize the new type away as it's never instantiated, nor used beyond ctfe reflection i.e. 0 runtime overhead. 3) It trivially allows using the built-in traits system everyone already is familiar with. but I wonder if one can do better with a mixin template, accessing it's "parent"...
Mar 25 2012
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 I think the liability here is that b needs to appear in two places.
Andrei, how about this: import std.stdio; import std.conv; struct NonSerialized(T) { enum isNonSerialized = true; T payload; alias payload this; } struct Foo { this(int x, string name, string lastName, string extra) { this.x = x; this.name = name; this.lastName = lastName; this.extra = extra; } int x; NonSerialized!string name; NonSerialized!string lastName; string extra; } string serialize(T)(T input) { string result; foreach (i, field; input.tupleof) { static if (skipSerialize!(typeof(field))) result ~= to!string(typeof(field).init) ~ "\n"; else result ~= to!string(field) ~ "\n"; } return result; } template skipSerialize(T) { enum bool skipSerialize = hasValidMember!(T, "isNonSerialized"); } template hasValidMember(T, string member) { static if (__traits(hasMember, T, member)) { enum bool hasValidMember = mixin("T." ~ member); } else enum bool hasValidMember = false; } void main() { Foo foo = Foo(10, "Foo", "Bar", "Doo"); string bin = serialize(foo); writeln(bin); } Note that I've had to make a constructor because I can't implicitly assign a string to a NonSerialized!string inside of a struct literal (but I think this is just a DMD frontend bug).
Mar 22 2012
next sibling parent "CTFE-4-the-win" <CTFE 4the.win> writes:
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:
 On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> 
 wrote:
 I think the liability here is that b needs to appear in two 
 places.
Andrei, how about this: Note that I've had to make a constructor because I can't implicitly assign a string to a NonSerialized!string inside of a struct literal (but I think this is just a DMD frontend bug).
Now, this is pure beauty. :)
Mar 22 2012
prev sibling parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:
 On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> 
 wrote:
 I think the liability here is that b needs to appear in two 
 places.
Andrei, how about this: Note that I've had to make a constructor because I can't implicitly assign a string to a NonSerialized!string inside of a struct literal (but I think this is just a DMD frontend bug).
Altering the type is not a reasonable approach for a generic annotation system. 1) It wrecks things that use template arguments, possibly special casing for certain types. 2) It makes it difficult to combine attributes. 3) It forces altering actual code to indicate an annotation, as opposed to just annotating it. exception of too little compile-time power to specify the arguments), I see no reason to attempt to reinvent it. I've never sufficient.
Mar 22 2012
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/22/12, Kapps <opantm2+spam gmail.com> wrote:
 1) It wrecks things that use template arguments, possibly special
 casing for certain types.
Yeah I've noticed things tend to break with alias this. Anyway if templates could be improved for these edge-cases then it's a benefit for everyone, regardless of annotations. :)
 2) It makes it difficult to combine attributes.
Difficult.. well maybe. You could use something like: Attribute!(int, NonSerialized, Encrypted) ID1, ID2; Or override opAssign and use bit masks: Attribute!int ID1 = NonSerialized | Encrypted; Attribute!int ID2 = NonSerialized | Encrypted; With attributes this would maybe be: NonSerialized Encrypted int ID1, ID2; I do like the attribute syntax more.
 3) It forces altering actual code to indicate an annotation, as
 opposed to just annotating it.
Yup.
Mar 22 2012
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 13:54:29 UTC, Steven Schveighoffer 
wrote:
 I think the closest anyone has come is Jacob, with his orange 
 library.  Maybe he can respond to this point.
I have some runtime reflection in web.d, but the way I do it is to build up the info at startup, and there's a special field in the class to hold it. simplified: struct Reflection { string name; ClassInfo[string] objects; FunctionInfo[string] functions; // etc } class ApiProvider { immutable(Reflection)* reflection; } immutable(Reflection)* prepareReflection(T)(T t) { Reflection* r = new Reflection(); foreach(member; __traits(allMembers, T)) static if(type..) r.typeInfo[member] = prepareReflection(getMember); } To call functions, it makes a wrapper that converts strings to the right types in a ParameterTypeTuple: functionInfo.caller = wrap!func; string delegate(string[][string]) wrap(alias func)() { return delegate string(string[][string] uriArgs) { foreach(type; ParameterTypeTuple!func) blah = to!typeof(blah)(uriArgs[name]); return to!string(func(args)); } } and so on. Of course, the real thing is a lot longer than this, but you can see the idea. Once it is all populated at startup, you can do: myobj.reflection.functions["cool"].call(decodeUriVars("sweet=yes&awesome=def")); and have it work.
Mar 21 2012
prev sibling parent reply "Martin Nowak" <dawg dawgfoto.de> writes:
 Note that one library that did attempt runtime reflection capability
 (flectioned) does all this at runtime, and does some really funky shit,
 like opening /proc/self/map on Linux, or requiring you to pass an
 OPTLINK map file. I don't look at these as "innovations" as much as I  
 do
 as workarounds.
Maybe there's a better approach than flectioned. Consider the language is frozen solid. How would you solve problems with it?
I think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -Steve
Just showing this because it never comes up that one can already implement runtime reflection using ModuleInfo.xgetMembers and TypeInfo_Struct.xgetMembers. module a; import std.stdio, std.variant; void main() { foreach(m; ModuleInfo) { if (m.name != "b") continue; // getMembers currently doesn't work for classes, // but it does for modules/structs auto get = cast(Variant function(string))m.xgetMembers; auto bfunc = get("bfunc").get!(size_t function()); writeln(bfunc()); } } module b; import std.variant; // a pointer to this method will end up in ModuleInfo xgetMembers Variant getMembers(string name) { Variant res; switch (name) { case "bfunc": return Variant(&bfunc); default: return Variant("member "~name~" not found"); } } size_t bfunc() { return 2; } It should be fairly easy to construct some helpers that make the compile time data digestible.
Mar 27 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-28 03:41, Martin Nowak wrote:

 Just showing this because it never comes up that one can already implement
 runtime reflection using ModuleInfo.xgetMembers and
 TypeInfo_Struct.xgetMembers.

 module a;
 import std.stdio, std.variant;

 void main()
 {
 foreach(m; ModuleInfo)
 {
 if (m.name != "b") continue;
 // getMembers currently doesn't work for classes,
 // but it does for modules/structs
 auto get = cast(Variant function(string))m.xgetMembers;
 auto bfunc = get("bfunc").get!(size_t function());
 writeln(bfunc());
 }
 }


 module b;
 import std.variant;

 // a pointer to this method will end up in ModuleInfo xgetMembers
 Variant getMembers(string name)
 {
 Variant res;
 switch (name)
 {
 case "bfunc": return Variant(&bfunc);
 default: return Variant("member "~name~" not found");
 }
 }

 size_t bfunc()
 {
 return 2;
 }

 It should be fairly easy to construct some helpers that make the compile
 time data digestible.
I had no idea that xgetMembers work at all. But as the comment says, it doesn't work for classes. -- /Jacob Carlborg
Mar 28 2012
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 11:09 AM, Steven Schveighoffer wrote:
 On Tue, 20 Mar 2012 11:17:02 -0400, Andrei Alexandrescu
 <SeeWebsiteForEmail erdani.org> wrote:

 Perhaps we should add a field of type Variant[string].
1. Variant is not (and shouldn't be IMO) part of druntime. 2. This is *static* data that should be available at compile time. D's runtime wasting cycles filling in static data (including lots of heap allocations) seems like a big enough detriment that compiler help is warranted.
Forgot to mention - I snipped these points because I agree with them. Andrei
Mar 20 2012
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Tuesday, 20 March 2012 at 15:17:04 UTC, Andrei Alexandrescu 
wrote:
 Also, as I mentioned, the availability of the easy escape hatch 
 of adding a language feature thwarts creativity.
I agree entirely. Usually, when I see threads like this, my response is "challenge accepted". But, on this problem, my answer is, unfortunately, "challenge failed". I want to annotate declarations for my web.d, where you can write a class and have D do a bunch of work for you in creating all kinds of stuff. Let me do up a quick example: === struct MyType { int num; string name; string desc; Element makeHtmlElement() { auto e = Element.make("div", desc).addClass("my-type"); return e; } } class MySite : ApiProvider { MyType getSomething(int someNumber, string name, Text description) { return MyType(someNumber, name, description); } void savePhoneNumber(string name, string phoneNumber) { _checkLoggedIn(); ensureGoodPost(); _doSave(); } void _doSave() { yada yada yada } } === Now, when you go to mysite.com/get-something, it will generate a form like this: <form method="GET"> Some Number: ____________ Name: ____________ Description: ____________ ____________ ____________ [Get Something] </form> It pulls this info from the D code - it uses an alias template param to the method, then parses .stringof to get the names and default params. (It then mixes in the default to get a D type, which doesn't work on custom types since they aren't in the right scope, but hey, it works in a lot of cases.) When possible, it uses the type of the parameter to specialize the form. This comes from ParameterTypeTuple. Here, it recognizes struct Text{} as being a textarea. tbh, I'd prefer to use note(Textarea(rows)), but this works, and alias this to string makes it pretty natural to work with too. Moreover, I wrote method="GET". It pulls this from the method name. Since it starts with get, it doesn't require http post for it. So far, so good. Let's look at the next method, savePhoneNumber. This !startsWith("get"), so it generates a POST form, with the same kind of thing: Name: _________ Phone Number: _________ [Save Phone Number] Hey, it works. But, I'd like to do even better. Could we do validation? Well, yeah, I think we could. struct PhoneNumber { enum _validationRegex = "[0-9]+"; } Boom. What about an example text, though? You could use that same enum method, but you would need a different type for each example. If you have two "string name" and in one case, you want it to be "e.g. Bob Smith" and in the other, you want "e.g. Downtown Office", you'd have to make separate structs for them, despite it just being strings in every other way. That's one big case where the compile time annotation comes through: void savePhoneNumber note(ExampleText("e.g. Downtown Office") string name, PhoneNumber phoneNumber) { } To do that with a mixin, you might: mixin(ExampleText!(savePhoneNumber, "name", "downtown")); void savePhoneNumber(string name, ...) {...} and have that yield: enum _attribute_DsavePhoneNumberMangle_param_name_FILE_LINE_type = ExampleText("downtown"); ok, this is a bit of a pain in the ass, but I think it'd work... (the file_line_type stuff is so you can have multiple things of the same type. Moderately fragile! But, it'd work.) The pain gets bad down the line though. Renaming things means repetition. Separate declarations might get lost as you shuffle the order around. But, not too awful... Until you get into more functions. Enter arsd.web.prepareReflection and makeJavascriptApi: foreach(member; __traits(allMembers, yourclass)) // build a runtime map and corresponding bindings for other languages Should our enum be included here? Should this include code to detect the annotations and translate them to the native target language? How much code is this going to take? What if I use a third party lib? Will it have to be hacked to ignore these extra names too? Well, we can work around this too. web.d has a convention where static if(name[0] == '_') { don't process }. (This is actually a workaround for a lack of __traits(getProtection) though. We already have D keywords that do this: public and/or export. But, there's currently no way to check for these, so I settled for a naming convention. This is what _doSave() demonstrates in the example. I'm hoping to do a dmd pull request to add getProtection at some point though.) We could all agree on a naming convention, in theory. In practice, I don't know. If I could check export || public, I might drop the underscore. Another problem is how do we get this data? We have an alias of the method... but not it's parent. Well, there's __traits(getParent). I think we could actually do that. But the idea of a parent reminds me of another problem. This is a class, the method is virtual. What if a subclass overrides it and wants to tweak the annotation? How will we handle annotation inheritance? Overloading is kinda solved via mangleof (I think, I haven't tried this yet, but in principle it would work), but inheritance is a whole other bag. Perhaps we do ClassName_methodName and in the search go up the base class tree? I don't know... I think we *could* make it work at this point, but it depends on an agreed naming convention, where we (essentially) embed little DSLs in member names, packing all kinds of info in there to work correctly. Piles upon piles of string processing code that we'd all have to agree on for reflection work, with complications around almost every corner. If we solve overloading and inheritance, what will be next? Embedding info directly in names is the easiest, but a bit problematic too: getSomething() What if I want it to be http GET, but be named say, "something-viewer"? Currently, I implement that as two functions: getSomething() for machines to access and alias getSomething somethingViewer; for human users. It isn't ideal, but again, it works. (The ensureGoodPost() function does a runtime check against CSRF. That's a potential candidate for an annotation too, but the exception works perfectly fine too, so meh.) Another thing I'd like, in the same vein as the ExampleText, is Hideable. Again, not really a type, though it could be with enough special case code added throughout to handle it. But, this would be a parameter that, if given, is hidden instead of showed: void changeName( note(Hide) int id, string name) {} link to change-name?id=1, and then you don't have to show the ID in the automatically generated form. Don't want to always do this though: send-message?message=example+message we *want* that to be editable... (In practice, right now, I make the form another function and modify it myself. That's several lines for something that could be done generically though, which is pain.) Well, I was going to go on longer, but I think we have enough here to draw a conclusion. Can we do it? Well, I kinda think we can. Hell, I already do like 3/4 of what I want in web.d With the power of D, we could make all kinds of weird stuff encoded as strings, stored as variable names. But, it is hard to use at most levels. Writing the annotations is a long, repetitive process. Processing them takes a lot of fragile code. Even /ignoring/ them takes a lot code, and that's what, to me, pushes it over into compiler territory. Compiler provided annotations are opt-in and backward compatible. If you want them, they're there, and if you don't, they aren't in the way like magic enums would be.
Mar 20 2012
parent sclytrack <sclytrack hotmail.com> writes:
On 03/20/2012 05:31 PM, Adam D. Ruppe wrote:
 Even /ignoring/ them takes a lot code, and that's
 what, to me, pushes it over into compiler territory.
vote++
Mar 20 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 20/03/2012 16:17, Andrei Alexandrescu a écrit :
 On 3/20/12 12:50 AM, Kapps wrote:
 On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:
 On 3/19/12 3:55 PM, F i L wrote:
 I think this could get tricky for the compiler to confidently use given
 that the mixed in enums can collide with existing members (although not
 likely). Also, if objects are not identified as being unique marked (in
 the compilers eyes), what specific attribute post-fixes will it be
 searching for on enums members?
Not a big issue, the name could always contain a uuid which makes collision practically impossibile. Andrei
And with parameters? Or how about run-time reflection, which is something that should be added eventually for attributes as well (that is, typeid(Blah).getCustomAttributes())? We can't manually add things into the TypeInfo.
Perhaps we should add a field of type Variant[string].
 This is an already ridiculously hackish approach, and
 it *does not* work for anything besides trivial applications. It is
 completly unreasonable to expect everything to be done in a library; the
 compiler exists for a reason and at some point you have to draw the
 line.
I'm afraid I disagree. A targeted language feature definitely makes a library approach inferior, by definition. But adding features is cheating, like printing money is for a government: very tempting, with apparently good short-term benefits, but with devastating cumulative effects. Also, as I mentioned, the availability of the easy escape hatch of adding a language feature thwarts creativity. Nobody will care to think about and come with idioms that use the language to do great things, if they know a language feature could be always added that makes things "nicer". I'm not saying this particular feature should or should not be in the language, but I wish our community exercised considerably more restraint when it comes about adding new language features.
That feature has been added to java with great benefit. I think the underlying need here is AOP. Adding annotation to java had great benefice in introducing AOP capabilities to the language. See : http://projectlombok.org/ Considering that D has great compile time capabilities, this is somewhere we want to go. Additionnaly, this is something that remove other capabilities, and replace them by lib support. For example, with synchronized, I could specify the phobos that I want that method or class to be synchronized, and the lib is able to add the needed code to ensure that functionality at compile time. I have to say that the whole idea of attaching property to instance doesn't make any sense to me - Walter make a good point on that I think, so I will not repeat his arguments.
Mar 20 2012
prev sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/19/12, F i L <witte2008 gmail.com> wrote:
 Also, if objects are not identified as
 being unique marked (in the compilers eyes), what specific
 attribute post-fixes will it be searching for on enums members?
I'm really not talking about general-purpose attributes, I was commenting on a specific serialization example. :) Also I've no idea what "objects being unique marked" means.
Mar 19 2012
parent "F i L" <witte2008 gmail.com> writes:
Andrej Mitrovic wrote:
 Also I've no idea what "objects being unique marked" means.
I meant that Attribute metadata is distinct data in the compilers eyes, ie, no name magic, just attribute lookup.
Mar 19 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/19/12 3:44 PM, Andrej Mitrovic wrote:
 On 3/19/12, Jacob Carlborg<doob me.com>  wrote:
 * Can be repeated on several fields (with the mixin you can only mixin
 "NonSerialized" once)
When I implemented NonSerialized for Vladimir's json library he made a suggestion to simply create enums of each field that is not to be serialized and encode it as "fieldname_nonSerialized". That would enable using a NonSerialized mixin multiple times. I've yet to implement it in that way, I ran into some odd bugs but I'll have a look at this soon. My implementation used a hash lookup table for the fields, but using enums would make the code even simpler. Basically: struct Foo { int x; string name; mixin(NonSerialized!name); string lastName; mixin(NonSerialized!lastName); } and this would expand to: struct Foo { int x; string name; enum name_nonSerialized; string lastName; enum lastName_nonSerialized; } So all you'd have to do is use compile-time introspection and a little bit of string processing to figure out if a field should be serialized or not.
I salute creative uses of the language over defining new features. Andrei
Mar 19 2012
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Mar 19, 2012 at 04:00:00PM -0500, Andrei Alexandrescu wrote:
 On 3/19/12 3:44 PM, Andrej Mitrovic wrote:
[...]
So all you'd have to do is use compile-time introspection and a
little bit of string processing to figure out if a field should be
serialized or not.
I salute creative uses of the language over defining new features.
+1. T -- Life is too short to run proprietary software. -- Bdale Garbee
Mar 19 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-19 22:00, Andrei Alexandrescu wrote:

 I salute creative uses of the language over defining new features.

 Andrei
Sure, just as well as you can do object oriented programming in C, but it is a mess. So is this. -- /Jacob Carlborg
Mar 19 2012
prev sibling next sibling parent reply dennis luehring <dl.soluz gmx.net> writes:
and how to add attribute parameters like DoSerialize(type=packed) for 


http://en.wikipedia.org/wiki/Java_annotation

Am 19.03.2012 22:00, schrieb Andrei Alexandrescu:
 On 3/19/12 3:44 PM, Andrej Mitrovic wrote:
  On 3/19/12, Jacob Carlborg<doob me.com>   wrote:
  * Can be repeated on several fields (with the mixin you can only mixin
  "NonSerialized" once)
When I implemented NonSerialized for Vladimir's json library he made a suggestion to simply create enums of each field that is not to be serialized and encode it as "fieldname_nonSerialized". That would enable using a NonSerialized mixin multiple times. I've yet to implement it in that way, I ran into some odd bugs but I'll have a look at this soon. My implementation used a hash lookup table for the fields, but using enums would make the code even simpler. Basically: struct Foo { int x; string name; mixin(NonSerialized!name); string lastName; mixin(NonSerialized!lastName); } and this would expand to: struct Foo { int x; string name; enum name_nonSerialized; string lastName; enum lastName_nonSerialized; } So all you'd have to do is use compile-time introspection and a little bit of string processing to figure out if a field should be serialized or not.
I salute creative uses of the language over defining new features. Andrei
Mar 19 2012
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 1:22 AM, dennis luehring wrote:
 and how to add attribute parameters like DoSerialize(type=packed) for


 http://en.wikipedia.org/wiki/Java_annotation
One more argument to the template? Andrei
Mar 20 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-19 22:00, Andrei Alexandrescu wrote:

 I salute creative uses of the language over defining new features.

 Andrei
The actual point of user defined attributes it to be able to create domain specific attributes so we don't have to add new features to the language. This is also to have a sane syntax for the attributes. I think in several cases D could have chose to implement a more general feature but instead implemented a very specific feature or hack. Example: If D supported passing delegates to a function after the function call several language features could have been implemented in the library instead. void synchronized (void delegate () dg) { try { lock(); dg(); } finally unlock(); } synchronized { // do something } In the same way foreach can be implemented in a library function: void foreach (T) (T[] arr, void delegate (T) dg) { for (size_t i = 0; i < arr.length; i++) dg(arr[i]); } auto arr = [3, 4, 5]; foreach (arr ; e // this is the parameter to the delegate) { writeln(e); } -- /Jacob Carlborg
Mar 20 2012
parent reply "F i L" <witte2008 gmail.com> writes:
Jacob Carlborg wrote:
 If D supported passing delegates to a function after the 
 function call several language features could have been 
 implemented in the library instead.

 void synchronized (void delegate () dg)
 {
     try
     {
         lock();
         dg();
     }
     finally
         unlock();
 }

 synchronized {
     // do something
 }

 In the same way foreach can be implemented in a library 
 function:

 void foreach (T) (T[] arr, void delegate (T) dg)
 {
     for (size_t i = 0; i < arr.length; i++)
         dg(arr[i]);
 }

 auto arr = [3, 4, 5];

 foreach (arr ; e // this is the parameter to the delegate) {
     writeln(e);
 }
That's pretty slick. D would need delegate inlining + forceInline before foreach could be expressed this way though.
Mar 20 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-20 10:49, F i L wrote:

 That's pretty slick. D would need delegate inlining +  forceInline
 before foreach could be expressed this way though.
Yes, but currently, as far as I know, the delegate passed to opApply is not inlined. -- /Jacob Carlborg
Mar 20 2012
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-19 21:44, Andrej Mitrovic wrote:

 When I implemented NonSerialized for Vladimir's json library he made a
 suggestion to simply create enums of each field that is not to be
 serialized and encode it as "fieldname_nonSerialized". That would
 enable using a NonSerialized mixin multiple times.

 I've yet to implement it in that way, I ran into some odd bugs but
 I'll have a look at this soon. My implementation used a hash lookup
 table for the fields, but using enums would make the code even
 simpler. Basically:

 struct Foo
 {
      int x;
      string name;
      mixin(NonSerialized!name);
      string lastName;
      mixin(NonSerialized!lastName);
 }

 and this would expand to:
 struct Foo
 {
      int x;
      string name;
      enum name_nonSerialized;
      string lastName;
      enum lastName_nonSerialized;
 }

 So all you'd have to do is use compile-time introspection and a little
 bit of string processing to figure out if a field should be serialized
 or not.
Yeah, but that will complicate the retrieval of the information. -- /Jacob Carlborg
Mar 19 2012
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/19/12, Jacob Carlborg <doob me.com> wrote:
 Yeah, but that will complicate the retrieval of the information.
What is so complicated about extracting fields? Just iterate via .tupleof: module test; import std.stdio; import std.conv; struct Foo { int x; string name; mixin NonSerialized!name; string lastName; mixin NonSerialized!lastName; string extra; } mixin template NonSerialized(alias field) { mixin("enum __attribute_nonSerialized_" ~ field.mangleof ~ ";"); } string serialize(T)(T input) { string result; foreach (i, field; input.tupleof) { static if (skipSerialize!(T, input.tupleof[i].mangleof)) result ~= to!string(typeof(field).init) ~ "\n"; else result ~= to!string(field) ~ "\n"; } return result; } template skipSerialize(T, string mangle) { enum bool skipSerialize = __traits(hasMember, T, "__attribute_nonSerialized_" ~ mangle); } void main() { Foo foo = Foo(10, "Foo", "Bar", "Doo"); string bin = serialize(foo); writeln(bin); } And by using .init instead of just skipping serialization altogether you can unserialize at a later point even if you end up removing some of the mixins, so you can have some binary-compatibility there.
Mar 19 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-19 22:51, Andrej Mitrovic wrote:
 On 3/19/12, Jacob Carlborg<doob me.com>  wrote:
 Yeah, but that will complicate the retrieval of the information.
What is so complicated about extracting fields? Just iterate via .tupleof:
It wasn't actually that much more complicated. But now there is one extra enum for each NonSerialized field. -- /Jacob Carlborg
Mar 20 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 18/03/2012 05:49, Walter Bright a écrit :
 On 3/17/2012 9:12 PM, F i L wrote:
 Walter Bright wrote:
 I also do not get why per-instance attributes even exist, as I agree
 that's
 what fields are for.
Attributes are per-type (type properties, methods, etc), not per-instance, and According to http://msdn.microsoft.com/en-us/library/z919e8tw(v=vs.80).aspx attribute objects aren't constructed until reflected upon.
My impression is it is just obfuscation around a simple lazy initialization pattern. While I can see the abstraction usefulness of compile time attribute metadata, I am having a hard time seeing what the gain is with runtime attributes over more traditional techniques.
The attribute itself doesn't do anything at runtime. But the attribute should be able to modify what is qualified before it is compiled. With that model it is much simpler, it open the door to AOP, and it allow us to implement as lib many feature that would have required compiler support before. AS mentioned deprecated, synchronized and override (including the recent propagation of safe, pure, nothrow that have made so much noise) could have been implemented with no compiler changes with a good property/ annotation support. This tells us quite a lot about its usefulness and how much powerful it is. Probably as powerful as templates are, and close to nuclear power combined with template.
Mar 20 2012
parent reply James Miller <james aatch.net> writes:
Sorry for not quoting, but I'm not sure who to quote...

I think people are over-thinking this issue. Adam's initial proposal
was simply to be able to add compile-time checkable data to data
structures and functions. I think this works well at this level. As he
demonstrated, string mixins and ctfe only get you so far in this
instance.

The idea that in order to replicate this functionality, you need to
actually parse the D code to get what you want, is horrific. Relying
on naming conventions for functionality is horrendous, especially when
a simple  note(data1,data2=val) can solve all of the issues. And if
they're only compile time, then it can all be discarded when you get
to code-gen, and if you deliberately limit the scope of them, then you
aren't fostering the "new language features will solve all my
problems".

Looking at other languages is a good start for ideas, but ultimately
we need a D-flavoured solution, and using simple `__trait`-inspectable
annotations is a good way of doing it. As Teoh said, think of it as a
hook into Typeinfo, allowing the programmer to alter it at compile
time.

I don't care about Aspect-Oriented programming, or anything like that,
I just want to be able write:

 note(GET,POST)
auto myClassFunction(arg1, arg2, arg3) { ... }

without having to use templates that break inheritance, or doing
checks in the function for the request type, that could be handled in
a library. There's already a mechanism for seeing what attributes that
function has, so why can't I have some code that does this:

template hasAnnotation(alias data, string an) {
    enum hasAnnotation = __traits(hasAnnotation, data, an);
}

(Or something, my template-fu leave something to be desired, not the
point though). It already looks like idiomatic D code.

Its not some big change that massively overhauls the way code is
written, the same way annotations in Java haven't meant that people
suddenly write Java completely differently. They do what they look
like they do, they add some extra information to the "thing" in
question that allow for some more advanced decision making.

Going back to the Web-based example, if I use an opDispatch system to
forward requests to the appropriate controller, I can use compile-time
reflection to generate checking code for the handlers, based , this
means that a simple annotation makes reading the function code much
simpler, since boiler-plate checking code has been removed, I can
implement a white-list based policy for request-type access, rather
than a blacklist that using isPost or isGet methods to check
in-handler. This is more secure, since if I forget to add /any/
annotations, then it wont receive /any/ requests. Lo and behold, I can
now have secure inter-controller communication without needing 1. a
naming convention, 2. an access control list or 3. boilerplate code.

I could go on a similar rant about serialization, but I think my point is made.

deadalnix is talking about being able to re-write language features in
terms of this system, but that's taking it too far, like a lot of
people, he wants D to be all thing to all people. The original idea
was to just to be able to add simple attributes to functions. Other
people sought to extend this unnecessarily, but everything else is
just templates, mixins and CTFE, nothing else.

The real discussion that need to be going on is: How far do notes
extend? (can you have arbitrary expressions annotated?) What data can
be packed into annotations? (Can i put any data into annotations, or
only internal types?) Syntax details, obviously using the  -syntax,
but specific naming, so  note, or  annotation? for example.
Scoping/Collision, I don't think this will be an issue, but if two
libraries are looking for the same annotation name, then there could
be some issues.


don't think it is needed for D, everything that those annotations
cover can be done using CTFE and mixins, with the minor addition
above.

--
James Miller
Mar 20 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 00:03:28 UTC, James Miller wrote:
 template hasAnnotation(alias data, string an) {
     enum hasAnnotation = __traits(hasAnnotation, data, an);
 }
I'm actually thinking of identifying them by type. Types are well established in the language - scoping, namespaces, fields, and so on are all solved. Querying them is solved too - we can look over a TypeTuple and use is(typeof()) to get what we want. (using types as names instead of strings is the idea that came to me on my ride that prompted this thread - it is the missing piece in what I wanted here. Strings can conflict, but types already handle this.) So you'd just very simply do: struct MyAttribute { bool activated; } // note is just like doing internalTuple ~= MyAttribute(true) // and MyAttribute is of course just a plain old struct initializer note(MyAttribute(true)) int a; To check it: foreach(note; __traits(getNotes, member_a)) static if(is(typeof(note) == MyAttribute) { // do what you want here, ignore types you don't know } Of course, this is in a plain template. No implicit stuff, no modifying the ast. This data only comes up when you ask for it, and you ask for it in a plain old mixin template or something like that - existing language features.
 How far do notes extend?
I'd only put it on declarations (variables, classes, structs, enums, members, and function parameters.)
 What data can be packed into annotations?
Being able to use custom types is an important part of my idea here.
Mar 20 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 01:35, Adam D. Ruppe wrote:
 On Wednesday, 21 March 2012 at 00:03:28 UTC, James Miller wrote:
 So you'd just very simply do:

 struct MyAttribute { bool activated; }

 //  note is just like doing internalTuple ~= MyAttribute(true)
 // and MyAttribute is of course just a plain old struct
 initializer
  note(MyAttribute(true)) int a;

 To check it:

 foreach(note; __traits(getNotes, member_a))
 static if(is(typeof(note) == MyAttribute) {
 // do what you want here, ignore types you don't know
 }
That's basically my initial proposal and how annotations work in Java.
 What data can be packed into annotations?
Being able to use custom types is an important part of my idea here.
I think any type that a template can take (as a value) + other attributes/annotations. -- /Jacob Carlborg
Mar 21 2012
next sibling parent reply "Tove" <tove fransson.se> writes:
On Wednesday, 21 March 2012 at 08:08:12 UTC, Jacob Carlborg wrote:
 On 2012-03-21 01:35, Adam D. Ruppe wrote:
 On Wednesday, 21 March 2012 at 00:03:28 UTC, James Miller 
 wrote:
 So you'd just very simply do:

 struct MyAttribute { bool activated; }

 //  note is just like doing internalTuple ~= MyAttribute(true)
 // and MyAttribute is of course just a plain old struct
 initializer
  note(MyAttribute(true)) int a;

 To check it:

 foreach(note; __traits(getNotes, member_a))
 static if(is(typeof(note) == MyAttribute) {
 // do what you want here, ignore types you don't know
 }
That's basically my initial proposal and how annotations work in Java.
 What data can be packed into annotations?
Being able to use custom types is an important part of my idea here.
I think any type that a template can take (as a value) + other attributes/annotations.
With the mixin improvement proposal any arbitrarily complex feature can be implemented in the library, appearing to enjoy first class syntax with just 1 extra character penalty vs the compiler. #struct Foo { NonSerialized int x; NonSerialized int y; int z; } Just imagine the next step, if the CTFE interface was based on AST:s instead of strings...
Mar 21 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 08:29:23 UTC, Tove wrote:
 With the mixin improvement proposal any arbitrarily complex 
 feature can be implemented in the library, appearing to enjoy 
 first class syntax with just 1 extra character penalty vs the 
 compiler.
My main concern with the library implementation isn't syntax. The big question is still: where will you put the annotation data?
Mar 21 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 03/21/12 14:36, Adam D. Ruppe wrote:
 On Wednesday, 21 March 2012 at 08:29:23 UTC, Tove wrote:
 With the mixin improvement proposal any arbitrarily complex feature can be
implemented in the library, appearing to enjoy first class syntax with just 1
extra character penalty vs the compiler.
My main concern with the library implementation isn't syntax. The big question is still: where will you put the annotation data?
A (new) compile-time only (mutable) storage class. It's probably needed for other things too, to avoid compiler-specific pragmas+section-attributes+linker-magic to achieve the same effect. For run-time accessible custom attributes (if those are even necessary), the lib solution wouldn't be much different than a built-in one anyway. artur
Mar 22 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-22 15:31, Artur Skawina wrote:

 For run-time accessible custom attributes (if those are even necessary),
 the lib solution wouldn't be much different than a built-in one anyway.

 artur
Accessing the custom attributes at runtime are necessary. Serialization is one example that could take advantage of that. -- /Jacob Carlborg
Mar 25 2012
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 08:08:12 UTC, Jacob Carlborg wrote:
 That's basically my initial proposal and how annotations work 
 in Java.
Cool. I did not realize that.
Mar 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 15:02, Adam D. Ruppe wrote:
 On Wednesday, 21 March 2012 at 08:08:12 UTC, Jacob Carlborg wrote:
 That's basically my initial proposal and how annotations work in Java.
Cool. I did not realize that.
Here's my proposal: http://forum.dlang.org/thread/bccwycoexxykfgxvedix forum.dlang.org?page=3#post-jk2d4t:242ban:241:40digitalmars.com -- /Jacob Carlborg
Mar 21 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Wednesday, 21 March 2012 at 15:02:15 UTC, Jacob Carlborg wrote:
 Here's my proposal:
Ah yes, I think I did read that before. I'm not in love with the key=value or the explicit attribute on the classes, which is the two biggest differences we have. The key=value is nice, but it isn't in the language today, so it'd be a bunch of special code in the attribute parser in the compiler. If we were to have key=value syntax, I say we should do it generically (like C style struct initializers?) so it can work in regular code too, then pick it up naturally here. I'd prefer to use the existing language syntax in there so the implementation can literally be as simple as: if(token == TOKnote) { token.next(); annotations ~= parseExpression(); } D's existing struct syntax can handle arguments easily enough with the StructName("member", "member"); style. Reusing that here keeps the implementation simple, and it might be more flexible: enum MyNote = Something("with args", 2); note(MyNote) int foo; note(MyNote) bool bar; this would work too with the simple setup, since the enum is already handled by the compiler's regular code. Ditto with ctfe, which is an open hook for some potentially cool stuff down the line without any extra implementation. The other thing is attribute struct {} rather than just struct {}. I don't any benefit to that. If I want to reuse a regular, runtime struct to store info at compile time, why shouldn't I be able to do that? I'm thinking of note(X) as meaning simply: enum tmp = X; // evaluate ctfe, etc decl.annotations ~= tmp; // store it for later
Mar 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 17:00, Adam D. Ruppe wrote:
 On Wednesday, 21 March 2012 at 15:02:15 UTC, Jacob Carlborg wrote:
 Here's my proposal:
Ah yes, I think I did read that before. I'm not in love with the key=value or the explicit attribute on the classes, which is the two biggest differences we have. The key=value is nice, but it isn't in the language today, so it'd be a bunch of special code in the attribute parser in the compiler. If we were to have key=value syntax, I say we should do it generically (like C style struct initializers?) so it can work in regular code too, then pick it up naturally here.
Would "key: value" be possible. The syntax we have for AA literals?
 I'd prefer to use the existing language syntax in there
 so the implementation can literally be as simple as:

 if(token == TOKnote) {
 token.next();
 annotations ~= parseExpression();
 }


 D's existing struct syntax can handle arguments easily
 enough with the StructName("member", "member"); style.
 Reusing that here keeps the implementation simple,
 and it might be more flexible:

 enum MyNote = Something("with args", 2);
That would be nice.
  note(MyNote) int foo;
  note(MyNote) bool bar;
But I don't like the note syntax. I really would like this: MyNote int foo;
 The other thing is  attribute struct {} rather than just
 struct {}. I don't any benefit to that. If I want to
 reuse a regular, runtime struct to store info at compile
 time, why shouldn't I be able to do that?
I just throw that in because Java has it. Don't know if it's needed or not. Java has a kind of special syntax for default values with annotations. -- /Jacob Carlborg
Mar 21 2012
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 21 Mar 2012 13:47:56 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2012-03-21 17:00, Adam D. Ruppe wrote:
 The other thing is  attribute struct {} rather than just
 struct {}. I don't any benefit to that. If I want to
 reuse a regular, runtime struct to store info at compile
 time, why shouldn't I be able to do that?
I just throw that in because Java has it. Don't know if it's needed or not. Java has a kind of special syntax for default values with annotations.
Really, the only requirement for an annotation expression should be that it's evaluated at compile time. But we could see weird things with annotations if we don't specifically say what's an annotation and what is not. For example: // used as a normal datatype in most places struct Point2d { int X; int Y; } Point2d int x; // ?? or note(Point2d) int x; // ?? In my proposal, I specified that the annotation can be applied only to module-level functions (normal or templated), and the result of those functions is what gets saved. So with that capability, you should be able to store any CTFE-evaluatable struct: annotation auto makeAPoint() {Point2d p; return p;} makeAPoint int x; Which is as confusing as the above two examples, but at least *someone* declared it made sense to them ;) Note that the annotation function need not be stored in the EXE, it's only valid during CTFE. I look at it similar to in C++ being able to throw any arbitrary type. Sure, you can just avoid creating an exception type that wraps an int, but it may not make sense to anyone if you just throw an int. -Steve
Mar 21 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-21 19:32, Steven Schveighoffer wrote:

 In my proposal, I specified that the annotation can be applied only to
 module-level functions (normal or templated), and the result of those
 functions is what gets saved. So with that capability, you should be
 able to store any CTFE-evaluatable struct:

  annotation auto makeAPoint() {Point2d p; return p;}

  makeAPoint int x;
Does " makeAPoint int x;" expand to "Point2d p; return p;"? -- /Jacob Carlborg
Mar 21 2012
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Wed, 21 Mar 2012 17:10:06 -0400, Jacob Carlborg <doob me.com> wrote:

 On 2012-03-21 19:32, Steven Schveighoffer wrote:

 In my proposal, I specified that the annotation can be applied only to
 module-level functions (normal or templated), and the result of those
 functions is what gets saved. So with that capability, you should be
 able to store any CTFE-evaluatable struct:

  annotation auto makeAPoint() {Point2d p; return p;}

  makeAPoint int x;
Does " makeAPoint int x;" expand to "Point2d p; return p;"?
What I envision is that makeAPoint translates to the CTFE engine calling makeAPoint with no arguments. This returns a Point2d default initialized. That result is stored in the TypeInfo (or ModuleInfo) in the appropriate location for the 'x' member. Then you can query this using runtime or compile-time functions later on, using the name "makeAPoint". So in effect, you can use any struct or type that can be constructed at compile-time. But it allows you to specify which types (and in which ways they can be constructed) can be used as annotations. It also allows you to name the way that type is used. It also could give you a way to use the same type for different annotations, discernible by name. -Steve
Mar 21 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-03-21 22:32, Steven Schveighoffer wrote:

 What I envision is that  makeAPoint translates to the CTFE engine
 calling makeAPoint with no arguments. This returns a Point2d default
 initialized. That result is stored in the TypeInfo (or ModuleInfo) in
 the appropriate location for the 'x' member.

 Then you can query this using runtime or compile-time functions later
 on, using the name "makeAPoint".
That sounds like a pretty good idea. Many good ideas have popped up in this thread. It's hard to know which would work best. -- /Jacob Carlborg
Mar 21 2012
prev sibling parent Manu <turkeyman gmail.com> writes:
On 18 March 2012 05:04, Kapps <opantm2+spam gmail.com> wrote:

 On Sunday, 18 March 2012 at 01:48:07 UTC, Walter Bright wrote:

 On 3/17/2012 6:39 PM, Manu wrote:


 same set
 of issues associated.
types.
It has threadlocal using the [ThreadLocal] attribute which gets implemented by the compiler. class/struct/method/field/**parameter. I don't see this being a problem. I can't think of good use cases where an attribute/annotation should be per-instance at all, particularly with the compile-time power that D has, be done at compile-time, and I think that's acceptable until/if runtime reflection is put in. If it is needed, it can live inside TypeInfo/MethodInfo, but is (imo) absolutely not needed on a per-instance basis. This is the job of fields or interfaces.
I think that's a fair call. I wonder why Java feels the need for stateful attributes. I have made extensive use of java attributes that have been stateful. Java's persistence api's rely on stateful attributes a lot, but I think that can be done in other ways in D. Compile-time attributes sound like a great start, it's much simpler. But they do need to be able to 'do stuff', ie, add some property/method to a field (using the field its self as the context pointer), not just simply 'tag' it.
Mar 18 2012
prev sibling parent "Paulo Pinto" <pjmlp progtools.org> writes:

else is available:

RAII            -> using ()
immutable -> achieved via cons/readonly (good enough for most purposes)
ThreadLocal -> ThreadLocal<T> class

"Walter Bright"  wrote in message news:jk3esn$1697$1 digitalmars.com...

On 3/17/2012 6:39 PM, Manu wrote:

 set
 of issues associated.
Mar 19 2012
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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.
Mar 16 2012
prev sibling next sibling parent reply Piotr Szturmaj <bncrbme jadamspam.pl> writes:
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;
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.
Mar 16 2012
parent Johannes Pfau <nospam example.com> writes:
Am Fri, 16 Mar 2012 16:21:41 +0100
schrieb Piotr Szturmaj <bncrbme jadamspam.pl>:

  note(Serializable.yes) int a;
then direct Struct(constructor args..) might be better.
+1
 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).
+1
 1) how do you define what is and is not a valid attribute?
It should be any user-defined type that may be used at compile-time. For example UUID("...") should be available out of the box.
Or we could add a attribute attribute: attribute struct Test { this(int a) {}... } Test(99) string someTestVal;
 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("").
And all normal import rules should work for user defined attributes as well (renamed imports, static imports, ...).
Mar 16 2012
prev sibling next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Mar 16 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
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-gmtawdmaihzgxnvezf f: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.
Mar 16 2012
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/16/12, Adam D. Ruppe <destructionator gmail.com> wrote:
 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.
Yeah, but I would say if we had even better compile-time introspection we could have the freedom to implement any number of annotation implementations in library code. When you put something into the language you have to depend on C++ hackers to implement and then inevitably fix the upcoming bugs in the front-end (ICEs are an annoying blocker), and there's always that issue where the devs are against adding new features to an existing language feature. (say annotations were implemented, soon enough someone is going to complain it doesn't have enough functionality and that it needs to be extended). Personally I'd love it if we had more __traits and compile-time introspection abilities.
Mar 16 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 16/03/2012 19:23, Andrej Mitrovic a écrit :
 On 3/16/12, Adam D. Ruppe<destructionator gmail.com>  wrote:
 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.
Yeah, but I would say if we had even better compile-time introspection we could have the freedom to implement any number of annotation implementations in library code. When you put something into the language you have to depend on C++ hackers to implement and then inevitably fix the upcoming bugs in the front-end (ICEs are an annoying blocker), and there's always that issue where the devs are against adding new features to an existing language feature. (say annotations were implemented, soon enough someone is going to complain it doesn't have enough functionality and that it needs to be extended). Personally I'd love it if we had more __traits and compile-time introspection abilities.
And not just introspection, but modification would be king.
Mar 20 2012
prev sibling next sibling parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Friday, 16 March 2012 at 16:09:55 UTC, Andrei Alexandrescu 
wrote:
 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
This gets to an unreasonable amount of noise and complexity. First, you have the issue of using mixins. Using mixins is quite ugly. It's very tool unfriendly. It's not easily readable. It can mess up line numbers. It may or may not be limited in situations such as with parameters. In language solutions should be preferred for many things, but ultimately, not everything is a nail to hammer. Ultimately, I'd like to see being able to use any CTFE capable struct/class for an attribute with a constructor, ideally with access to the symbol it's defined on. This allows things like (if my unchecked code was correct): attribute struct WebForm { string FormName; this(string FormName) { this.FormName = FormName; static assert(is(typeof(Symbol) : struct)); static assert(__traits(getAllMembers, Symbol).length > 0); // TODO: Make sure at least one member set to Browsable(true). } } attribute struct Validate { string Pattern; this(string Pattern) { this.Pattern = Pattern; static assert(isValidRegex(Pattern)); } } WebForm("Account"); PostTo("Services/CreateAccount") SecureOnly(true) struct CreateAccountForm { Description("The name of your account. Must be between 3 and 12 letters."); Validate(r"\w{3,12}") string Username; Validate(r"\w+ \w+.\w+"); Description("The email address to associate with this account."); string Email; } While it's a bit of boiler plate to create the library and attributes, you only need to do it once. Then you can make a very nice form that automatically validates fields both clientside (where JS and/or CSS3 is supported) and serverside (when posted to Services/CreateAccount). You can verify a bunch of things at compile-time, including that the service exists or that the validation contains valid regex. And best of all, it's actually readable unlike if you went with a mixin approach. You would end up having 7 random mixins in that above form, and have to manually check each one to see if it's creating code or just creating an attribute. This way, you can see the and know it's an attribute immediately. Another thing that doesn't come up yet but may in the future: reflection. It seems worrying to have random mixins adding reflection info, as that's a job for the compiler. It knows about when reflection is enabled, it knows how much it needs to generate, if things exist after optimization, etc. There's no point leaving that to a mixin and having to keep it in sync with the compiler. This way though, there's no need. The reflection data would just have an annotations property that can be accessed.
Mar 16 2012
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:
  WebForm("Account");
  PostTo("Services/CreateAccount")
  SecureOnly(true)
 struct CreateAccountForm {
That kind of turns D from a structural to a declarative language. :p
Mar 16 2012
parent "Kapps" <opantm2+spam gmail.com> writes:
On Saturday, 17 March 2012 at 00:54:02 UTC, Andrej Mitrovic wrote:
 On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:
  WebForm("Account");
  PostTo("Services/CreateAccount")
  SecureOnly(true)
 struct CreateAccountForm {
That kind of turns D from a structural to a declarative language. :p
Web design is quite a declarative thing. :) The code is already written for you, why bother writing it again instead of just writing it once and passing along essentially parameters to it. Anywho, something like that is a somewhat extreme case. Basic things include serializable or notserialized (or serialized(false)). With the topic of dynamic/scripting languages, other things can include scripted to indicate a method or class can be accessed from the script, or scripted(Security.low) to indicate user scripts can access it and not just game scripts. This prevents having to make annoying bindings and communicate what's passed in to the script compiler. Instead, you can find out everything that can be passed in at compile-time, and upon script compilation you can verify the script has access to all of these methods, then statically call them without any of the performance hit you would normally have by using scripting, yet with the safety.
Mar 16 2012
prev sibling next sibling parent Manu <turkeyman gmail.com> writes:
On 17 March 2012 02:53, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:

 On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:
  WebForm("Account");
  PostTo("Services/CreateAccount")
  SecureOnly(true)
 struct CreateAccountForm {
That kind of turns D from a structural to a declarative language. :p
And that's awesome in many situations! :) this, and it's an invaluable language feature. It adds so much simplicity to the tools code.
Mar 17 2012
prev sibling parent Gor Gyolchanyan <gor.f.gyolchanyan gmail.com> writes:
What we're talking about here is subject-oriented programming: having
the attributes get enclosed into objects, rather then objects
enclosing attributes. And this is extremely useful, IMO.

On Sat, Mar 17, 2012 at 1:23 PM, Manu <turkeyman gmail.com> wrote:
 On 17 March 2012 02:53, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:
 On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:
  WebForm("Account");
  PostTo("Services/CreateAccount")
  SecureOnly(true)
 struct CreateAccountForm {
That kind of turns D from a structural to a declarative language. :p
And that's awesome in many situations! :) this, and it's an invaluable language feature. It adds so much simplicity to the tools code.
-- Bye, Gor Gyolchanyan.
Mar 17 2012
prev sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 16/03/2012 17:09, Andrei Alexandrescu a écrit :
 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
It is more tricky if the property isn't a simple attribute to read. Again, consider what is done with this simple example : http://projectlombok.org/ We have the opportunity here to introduce in D the concept of aspect oriented programming. This is HUGE. If you are afraid of the addition of a functionnality to the language, don"t worry, you are not just adding a functionnality, but a whole new paradigm. And, BTW, this would allow us to drop some functionalities that now can be provided by phobos (synchronized for example is an obvious one). Same goes for override, deprecated, and the fun thing is that we can implement our own to extends the language even more as lib. Even the propagation of pure, safe, nothrow and const that has been discussed recently can be done with that feature. If you are worried about introducing language features (I am) you should definitively introduce that one, because lot of features has already been included just because that one is lacking. Adding compile time information to a type is the visible part of the iceberg. Really.
Mar 20 2012
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/20/12 4:36 PM, deadalnix wrote:
 We have the opportunity here to introduce in D the concept of aspect
 oriented programming. This is HUGE. If you are afraid of the addition of
 a functionnality to the language, don"t worry, you are not just adding a
 functionnality, but a whole new paradigm.
I dabbled into AOP quite a bit, but I'm not all that jazzed about it. It's been around for quite a while but it has little to show for it. At the present it's rather unstructured and lacks a strong corpus of useful idioms. I'd say - interesting, but I wouldn't bet the farm on it. Andrei
Mar 20 2012
parent deadalnix <deadalnix gmail.com> writes:
Le 20/03/2012 22:36, Andrei Alexandrescu a écrit :
 On 3/20/12 4:36 PM, deadalnix wrote:
 We have the opportunity here to introduce in D the concept of aspect
 oriented programming. This is HUGE. If you are afraid of the addition of
 a functionnality to the language, don"t worry, you are not just adding a
 functionnality, but a whole new paradigm.
I dabbled into AOP quite a bit, but I'm not all that jazzed about it. It's been around for quite a while but it has little to show for it. At the present it's rather unstructured and lacks a strong corpus of useful idioms. I'd say - interesting, but I wouldn't bet the farm on it. Andrei
Honestly, AOP is quite hard to use right now because of the lack of language support. This is a chicken and egg issue. This is more and more used in Java, for good reasons. I think we will sooner or later talk about modern Java like we talk about modern C++ today. You are well placed to know how template have been implemented in C++ simply for generic, and what comes out of it. And how awesome that is. Many feature of the current D could have been implemented using AOP. I think that pretty much made the point. AOP is useful, and as a proof emulating it by compiler magic, when we cannot do without (synchronized, override, deprecated, as examples). If AOP wasn't useful, theses feature wouldn't have been included in D at all. I'm suggesting templates, your answering « everything is a object, so let's do generic, I don't see the point of templates ».
Mar 20 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-20 22:36, deadalnix wrote:

 It is more tricky if the property isn't a simple attribute to read.
 Again, consider what is done with this simple example :
 http://projectlombok.org/

 We have the opportunity here to introduce in D the concept of aspect
 oriented programming. This is HUGE. If you are afraid of the addition of
 a functionnality to the language, don"t worry, you are not just adding a
 functionnality, but a whole new paradigm.

 And, BTW, this would allow us to drop some functionalities that now can
 be provided by phobos (synchronized for example is an obvious one). Same
 goes for override, deprecated, and the fun thing is that we can
 implement our own to extends the language even more as lib.

 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.

 If you are worried about introducing language features (I am) you should
 definitively introduce that one, because lot of features has already
 been included just because that one is lacking.

 Adding compile time information to a type is the visible part of the
 iceberg. Really.
I couldn't agree more. -- /Jacob Carlborg
Mar 21 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 03/20/2012 10:36 PM, deadalnix wrote:
 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.
I'm sceptical. How would that work exactly?
Mar 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 11:17 AM, Timon Gehr wrote:
 On 03/20/2012 10:36 PM, deadalnix wrote:
 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.
I'm sceptical. How would that work exactly?
I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. Andrei
Mar 21 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :
 On 3/21/12 11:17 AM, Timon Gehr wrote:
 On 03/20/2012 10:36 PM, deadalnix wrote:
 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.
I'm sceptical. How would that work exactly?
I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. Andrei
That is the point. The property must be able to manipulate what is qualified. This is the point of AOP. If you let me some time, I could write a proposal. BTW, this is probably something we don't want to rush in, but I'm sure it definitively worth it. Have you seen what a project like lombok can do ?
Mar 21 2012
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 3/21/12 6:02 PM, deadalnix wrote:
 Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :
 On 3/21/12 11:17 AM, Timon Gehr wrote:
 On 03/20/2012 10:36 PM, deadalnix wrote:
 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.
I'm sceptical. How would that work exactly?
I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. Andrei
That is the point. The property must be able to manipulate what is qualified. This is the point of AOP.
Problem is you'd need a ton of hooks to e.g. prevent mutation in const methods, or do attribute inference as the compiler does now.
 If you let me some time, I could write a proposal. BTW, this is probably
 something we don't want to rush in, but I'm sure it definitively worth it.
Honest, I have zero enthusiasm for adding AOP to D right now. But I agree it would be an interesting project to see what it would take to implement something of the power of pure or const starting from zero knowledge.
 Have you seen what a project like lombok can do ?
This? http://projectlombok.org/ I'll take a look, thanks. Andrei
Mar 21 2012
parent reply deadalnix <deadalnix gmail.com> writes:
Le 22/03/2012 03:05, Andrei Alexandrescu a écrit :
 On 3/21/12 6:02 PM, deadalnix wrote:
 Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :
 On 3/21/12 11:17 AM, Timon Gehr wrote:
 On 03/20/2012 10:36 PM, deadalnix wrote:
 Even the propagation of pure,  safe, nothrow and const that has been
 discussed recently can be done with that feature.
I'm sceptical. How would that work exactly?
I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. Andrei
That is the point. The property must be able to manipulate what is qualified. This is the point of AOP.
Problem is you'd need a ton of hooks to e.g. prevent mutation in const methods, or do attribute inference as the compiler does now.
 If you let me some time, I could write a proposal. BTW, this is probably
 something we don't want to rush in, but I'm sure it definitively worth
 it.
Honest, I have zero enthusiasm for adding AOP to D right now. But I agree it would be an interesting project to see what it would take to implement something of the power of pure or const starting from zero knowledge.
I think we have a misunderstanding here. I wasn't talking about reimplementing const or pure. I was talking about propagating them to overriden method (a way more easier task). I was talking about implementing the recently discussed behavior of override, not actually reimplementing const or pure. Not that that wouldn't be possible, but it would require a massive amount of work and a very cooperative compiler. Inference isn't possible with an attribute system. This is less impressive, but more realistic goal. And that is quite easy to achieve.
 Have you seen what a project like lombok can do ?
This? http://projectlombok.org/ I'll take a look, thanks.
Yes.
Mar 22 2012
parent "foobar" <foo bar.com> writes:
On Thursday, 22 March 2012 at 23:48:03 UTC, deadalnix wrote:

 Inference isn't possible with an attribute system.
It isn't possible simply because it wasn't implemented yet. nothing prevents us to add such a feature in the future. Could be a worthwhile enhancement for D3 or D4, given we actually have attributes implemented before then.
Mar 23 2012
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 16 Mar 2012 09:35:54 -0400, Adam D. Ruppe  
<destructionator gmail.com> 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;
I thought <symbol> was supposed to be a user-defined annotation. Otherwise, why did we introduce syntax? I'd rather see something like this: annotation serializable(bool x = true) {return x ? Serializable.yes : Serializable.no;} // implicit auto serializable int a; serializable(false) int b; Where annotations have to be CTFE-able (because they are constructed at compile-time) -Steve
Mar 16 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer 
wrote:
 I thought  <symbol> was supposed to be a user-defined 
 annotation.  Otherwise, why did we introduce  syntax?
idk, to "reduce" the number of keywords or somethiny. This is why I call it a mistake or missed opportunity right now though: property, safe, disable, system, and trusted have already made a claim on the syntax. Now, we have to work around that, which is why I'm thinking note(expression) rather than <something>.
 I'd rather see something like this:
I could live with that too, but I think it'd be harder to make happen due to potential clashes with the current thing is used for.
Mar 16 2012
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 16 Mar 2012 13:36:42 -0400, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer wrote:
 I thought  <symbol> was supposed to be a user-defined annotation.   
 Otherwise, why did we introduce  syntax?
idk, to "reduce" the number of keywords or somethiny.
Quote from TDPL (section 5.9.1 on page 156): "Attributes, always introduced with , are simple adornments specifying certain features for the symbol being defined. Some attributes are recognized by the compiler; some are defined and used by the programmer alone." I assumed this was true, and thought so even before TDPL came out. See http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP6
 This is why I call it a mistake or missed opportunity
 right now though:  property,  safe,  disable,  system,
 and  trusted have already made a claim on the  syntax.
I think those are just compiler-defined attributes. They aren't keywords in the sense that the parser doesn't treat them as special. They are just another type of symbol.
 I could live with that too, but I think it'd be harder
 to make happen due to potential clashes with the current
 thing   is used for.
meh, just don't use safe, property, disable, system, and trusted. I don't see a huge number of compiler-defined attributes springing up. Nor do I see a huge number of library or user-defined ones springing up. -Steve
Mar 16 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Friday, 16 March 2012 at 18:07:18 UTC, Steven Schveighoffer 
wrote:
 Quote from TDPL (section 5.9.1 on page 156):
Huh. There's nothing I can see in the compiler that even hints at this. The thing is just another storage class as far as it's concerned right now.
 Nor do I see a huge number of library or user-defined ones 
 springing up.
Yeah. I still really like my expression idea though, but thinking about your strategy, we could say: When you see , if it is a compiler word after it, parse it that way. Otherwise, try to get a function call out of it. Treat the function call as an enum and add it to the list. Thus, it is CTFE, namespaced, etc. like normal. The return value is appended to the declaration's attribute expression list (so the end result is the same as I was thinking.) I'm pretty sure that'd work..
Mar 16 2012
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 16 Mar 2012 14:33:37 -0400, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 On Friday, 16 March 2012 at 18:07:18 UTC, Steven Schveighoffer wrote:
 Quote from TDPL (section 5.9.1 on page 156):
Huh. There's nothing I can see in the compiler that even hints at this. The thing is just another storage class as far as it's concerned right now.
Right, the user-defined portion is not implemented.
 Nor do I see a huge number of library or user-defined ones springing up.
Yeah. I still really like my expression idea though, but thinking about your strategy, we could say: When you see , if it is a compiler word after it, parse it that way. Otherwise, try to get a function call out of it.
The way I'd like to see it work (and not being a compiler writer, I have no idea if/how this would work) is: When you see , call a CTFE function with that name. The compiler defines intrinsic functions property, disable, etc. which return intrinsic data types that get appended to the attribute list. Then the compiler recognizes those data types in the attribute list when deciding how things should be compiled. This might not be feasible given the current implementation, or how compilers are designed, I have no idea.
 The return value is appended to the declaration's
 attribute expression list (so the end result is the same
 as I was thinking.)
Yes. And I'd like to see the other intrinsic attributes appended (and queryable) as well. What we also may need in the annotation declaration is an alias of the thing being compiled (again, don't know how feasible this is) for example: annotation property(alias sym)() { static assert(is(sym == function)); return INTRINSIC.isProperty;} Maybe that's going too far :) -Steve
Mar 16 2012
prev sibling parent deadalnix <deadalnix gmail.com> writes:
Le 16/03/2012 18:36, Adam D. Ruppe a écrit :
 On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer wrote:
 I thought  <symbol> was supposed to be a user-defined annotation.
 Otherwise, why did we introduce  syntax?
idk, to "reduce" the number of keywords or somethiny. This is why I call it a mistake or missed opportunity right now though: property, safe, disable, system, and trusted have already made a claim on the syntax. Now, we have to work around that, which is why I'm thinking note(expression) rather than <something>.
 I'd rather see something like this:
I could live with that too, but I think it'd be harder to make happen due to potential clashes with the current thing is used for.
This isn't a problem because they'd be scoped to the module anyway. D have no top level names, except for the one provided by the compiler, and that is great !
Mar 20 2012
prev sibling next sibling parent Alvaro <alvaroDotSegura gmail.com> writes:
El 16/03/2012 14:35, Adam D. Ruppe escribió:

 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.
I like that. I learned about annotations a couple years ago in two contexts: - Hibernate in Java They come very handy and are hard to beat with current language features. [WebService] public class Service : WebService { [WebMethod] public string helloWorld() { return "Hello World"; } } What coult be a similar D with annotations?: note(WebService) class Service : WebService { note(WebMethod) string helloWorld() { return "Hello World"; } } and have some CTFE stuff generate the magic?
Mar 16 2012
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-03-16 14:35, Adam D. Ruppe wrote:
 On the ride over here today, I had a thought that
 I think neatly solves the user defined attribute
 question.
I would love to have user defined attributes in D but I'm not completely like your approach. I would go with something more like Java annotations. This is how I would like user defined attributes to work. Attributes can be applied to most declarations like: classes, fields, variables, functions, parameters and so on. Attributes are used with the following syntax: foo(key = "value", key2 = "value2) class Bar {} "foo" is the name of the attribute. In the parentheses is a key-value list passed to the attribute. The values can be of any type available at compile time. Attributes are defined as follows: We add a new attribute called " annotation" or " attribute". This attribute is applied to a class or struct to make it an attribute: attribute class foo { string key; string key2; } The keys the attribute accepts are declared in the class as either fields or methods (haven't decided yet). It's perfectly fine to have an attribute accepting no values which doesn't have to declare any keys: attribute class bar {} If an attribute only accepts one value the name of the value needs to be "value": attribute class fooBar { string value; } This allows to not have to specify the key when passing a value to the attribute: fooBar("asd") class Foo {} Accessing information about attribute should be possible both during compile time and runtime. At compile time "__traits" can be used: __traits(hasAttribute, bar, Foo); // returns true if the attribute "bar" as been applied to "Foo" __traits(getAttribute, bar, key, Foo); // returns the value of the key "key" This is all very similar to how it works in Java: http://docs.oracle.com/javase/1.5.0/docs/guide/language/annotations.html -- /Jacob Carlborg
Mar 17 2012
prev sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
On Friday, 16 March 2012 at 13:35:55 UTC, Adam D. Ruppe wrote:
 On the ride over here today, I had a thought that
 I think neatly solves the user defined attribute
 question.
Maybe there are other ways to think about user defined attributes. I'd like them to essentially be ways to extend the static type system in library code. I am thinking about things like the ones done by Dehydra and Treehydra (https://developer.mozilla.org/en/Dehydra https://developer.mozilla.org/en/Treehydra ) but using the D language itself instead of JavaScript. This asks for more __traits(...), to allow the D programmer (that is defining a new attribute) to use them to static verify more characteristics of the D code being annotated with the attributes. Bye, bearophile
Mar 18 2012