digitalmars.D - Proposal: user defined attributes
- Adam D. Ruppe (59/59) Mar 16 2012 On the ride over here today, I had a thought that
- Manu (29/44) Mar 16 2012 Surely the term you're looking for here is @annotate(...) ?
- Adam D. Ruppe (30/39) Mar 16 2012 Meh, I don't care that much about names. I went
- Manu (2/44) Mar 16 2012 Interesting approach, how will that affect 'thing's type?
- Adam D. Ruppe (16/17) Mar 16 2012 It will strictly be myAttribute!int, but alias this
- Manu (5/22) Mar 16 2012 I can see that it might work nicely in very simple cases, but it could g...
- Walter Bright (5/13) Mar 17 2012 Under the hood, where would that per-instance data be stored?
- Manu (9/27) Mar 17 2012 In Java+C# it just lives in the class somewhere. Put it right beside the
- Manu (5/36) Mar 17 2012 I tend to think of 'struct's as 'passive', and compile-time annotations
- Walter Bright (15/18) Mar 17 2012 I currently find the notion of runtime attribute state to be fraught wit...
- Manu (26/41) Mar 17 2012 After initialisation, before construction.
- Walter Bright (2/4) Mar 17 2012 C# doesn't have RAII, immutable, nor the notion of threadlocal/shared ty...
- Manu (5/12) Mar 17 2012 C# has immutable. How does RAII cause a problem here?
- Walter Bright (6/18) Mar 17 2012 Generally, supporting it tends to permeate the semantics of the language...
- Kapps (13/19) Mar 17 2012 It has threadlocal using the [ThreadLocal] attribute which gets
- Walter Bright (5/15) Mar 17 2012 That's a storage class, not a type constructor. In D, threadlocal/shared...
- Adam D. Ruppe (6/6) Mar 17 2012 Walter, how do you feel about the compile time
- Walter Bright (4/6) Mar 18 2012 I feel it has merit, but I think it is possibly more complex than necess...
- F i L (54/56) Mar 17 2012 Attributes are per-type (type properties, methods, etc), not
- F i L (8/13) Mar 17 2012 typo **@Cool(cool);
- Walter Bright (5/12) Mar 17 2012 My impression is it is just obfuscation around a simple lazy initializat...
- F i L (8/14) Mar 17 2012 I'm not sure exactly what you mean by runtime attributes. Do you
- Walter Bright (9/24) Mar 18 2012 Lazy initialization is a standard pattern. No special language features ...
- F i L (65/76) Mar 18 2012 In C#, no there isn't. Attributes are simply objects constructed
- F i L (34/37) Mar 18 2012 I found a way:
- Tove (4/17) Mar 18 2012 Is it not possible to alias a mixin to just one letter, and then
- F i L (14/32) Mar 18 2012 Sure, but there's still the issue of using attributes for
- Walter Bright (7/55) Mar 18 2012 Sorry, it still looks like standard lazy initialization. I don't know wh...
- Jacob Carlborg (62/64) Mar 18 2012 If we'll get user defined attributes that is accessible via runtime
- Walter Bright (9/34) Mar 18 2012 I'm sorry, I find this *massively* confusing. What is foo? Why are you
- F i L (59/65) Mar 18 2012 He's giving an example of serialize() below the code you quoted.
- dennis luehring (47/52) Mar 18 2012 attributes does not containing code - there just a (at-runtime)
- Tove (21/82) Mar 19 2012 Well I was thinking if we can go one step further than C#,
- dennis luehring (4/18) Mar 19 2012 i don't get the GC relation here?
- F i L (17/21) Mar 19 2012 C# doesn't support free-types (everything's wrapped up in
- dennis luehring (2/24) Mar 19 2012 sound to simple to fit in non trival cases
- Jacob Carlborg (4/23) Mar 19 2012 That would be cool.
- Manu (2/48) Mar 19 2012 +1 this post
- Jacob Carlborg (18/64) Mar 19 2012 It should have been:
- RivenTheMage (28/30) Mar 19 2012 Maybe this will help:
- Steven Schveighoffer (59/80) Mar 19 2012 I don't think we should go this far with attributes. If you want to sto...
- dennis luehring (2/83) Mar 19 2012
- Jacob Carlborg (26/91) Mar 19 2012 For the serialization example I think it's mostly a syntactic
- Andrej Mitrovic (29/31) Mar 19 2012 When I implemented NonSerialized for Vladimir's json library he made a
- F i L (6/45) Mar 19 2012 I think this could get tricky for the compiler to confidently use
- Andrei Alexandrescu (4/9) Mar 19 2012 Not a big issue, the name could always contain a uuid which makes
- F i L (5/18) Mar 19 2012 So the compiler would always search for enums with a specific
- Kapps (18/31) Mar 19 2012 And with parameters? Or how about run-time reflection, which is
- Andrei Alexandrescu (17/45) Mar 20 2012 I'm afraid I disagree. A targeted language feature definitely makes a
- Jacob Carlborg (6/26) Mar 20 2012 See my reply to one of your other posts:
- Andrei Alexandrescu (7/16) Mar 20 2012 I understand the intention there, but my point stands: let's first
- Piotr Szturmaj (2/10) Mar 20 2012 Please, do not transform D into a dynamic language.
- Andrei Alexandrescu (3/14) Mar 20 2012 Where's the "dynamic languages rok" crowd when you need it :o).
- deadalnix (2/17) Mar 20 2012 They are busy debugging what they coded sooooooo quickly and efficiently...
- Jacob Carlborg (4/8) Mar 20 2012 They left you behind after all those "d rox" comments :)
- deadalnix (2/10) Mar 20 2012 I was probably one of the most controversial part of the thread :D
- Jacob Carlborg (4/12) Mar 20 2012 Maybe you agreed on that. I still don't like it.
- Don Clugston (6/17) Mar 22 2012 I'm not sure who agreed, but I don't even think it would work. What
- F i L (7/15) Mar 20 2012 RON PAUL 2012!!!
- Steven Schveighoffer (28/44) Mar 20 2012 1. Variant is not (and shouldn't be IMO) part of druntime.
- Andrei Alexandrescu (8/40) Mar 20 2012 There is something nice about every new feature.
- deadalnix (3/5) Mar 20 2012 Easy, I already did that many times. I'd preprocess the source code with...
- Steven Schveighoffer (16/46) Mar 21 2012 Well, then let's not compare the hyperinflation of the economy to adding...
- Jacob Carlborg (19/26) Mar 21 2012 With Orange I'm doing everything with compile time reflection.
- Andrei Alexandrescu (13/40) Mar 21 2012 I think the liability here is that b needs to appear in two places, once...
- F i L (6/8) Mar 21 2012 struct A {
- F i L (9/17) Mar 21 2012 Also, if where meant how could you store multiple types, you
- F i L (1/2) Mar 21 2012 Typo, I meant: ["int":"a", "float":"b"];
- Andrei Alexandrescu (3/22) Mar 21 2012 Is this implemented, or a thought?
- F i L (4/5) Mar 21 2012 Just a thought (I even accidentally reversed the key-value
- Andrei Alexandrescu (4/9) Mar 21 2012 Wouldn't a better approach rely on a compile-time structure instead of a...
- F i L (6/16) Mar 21 2012 I'm not sure, you're understand of D's compiler-time structures
- Jacob Carlborg (4/8) Mar 21 2012 I'm using the serialization attributes at compile time.
- Jacob Carlborg (4/12) Mar 21 2012 Only the name of the variables are necessary in my serialization library...
- F i L (3/5) Mar 21 2012 Wouldn't mixing in an enum be more useful? You could use it at
- Jacob Carlborg (5/9) Mar 21 2012 I'm using a static const variable because it's compatible with D1. It
- Andrei Alexandrescu (6/14) Mar 21 2012 That's may be a problem because it makes it impossible to put together
- Adam D. Ruppe (56/58) Mar 21 2012 Oh god, I feel like the spawn of Hacktan.
- Adam D. Ruppe (40/42) Mar 21 2012 Another note on correctness btw, this thing is wrong
- Jacob Carlborg (4/12) Mar 21 2012 Exactly.
- Jacob Carlborg (21/30) Mar 21 2012 Yes, but that just looks ugly:
- Andrei Alexandrescu (4/17) Mar 21 2012 Well if the argument boils down to nice vs. ugly, as opposed to possible...
- Jacob Carlborg (5/7) Mar 21 2012 No, that's just one part on the problem. Read Adam D. Ruppe's posts in
- Manu (12/14) Mar 22 2012 By this logic, I might as well stick with C++. It's 'possible' to do
- Jacob Carlborg (4/15) Mar 25 2012 Yes, I completely agree.
- Ary Manzana (5/25) Mar 22 2012 Why don't you program everything with gotos instead of for, foreach and
- Tove (55/68) Mar 21 2012 In case my proof of concept which was posted in another thread
- Jacob Carlborg (5/18) Mar 25 2012 Just really ugly and it creates a new type, completely unnecessary if D
- Tove (11/30) Mar 25 2012 Well... "eye of the beholder"... I think that's exactly the
- Andrej Mitrovic (57/58) Mar 22 2012 Andrei, how about this:
- CTFE-4-the-win (2/12) Mar 22 2012 Now, this is pure beauty. :)
- Kapps (13/23) Mar 22 2012 Altering the type is not a reasonable approach for a generic
- Andrej Mitrovic (13/18) Mar 22 2012 Yeah I've noticed things tend to break with alias this. Anyway if
- Adam D. Ruppe (34/36) Mar 21 2012 I have some runtime reflection in web.d, but the way I do
- Martin Nowak (35/47) Mar 27 2012 Just showing this because it never comes up that one can already impleme...
- Jacob Carlborg (5/40) Mar 28 2012 I had no idea that xgetMembers work at all. But as the comment says, it
- Andrei Alexandrescu (3/11) Mar 20 2012 Forgot to mention - I snipped these points because I agree with them.
- Adam D. Ruppe (190/192) Mar 20 2012 I agree entirely. Usually, when I see threads like this,
- sclytrack (2/4) Mar 20 2012 vote++
- deadalnix (16/53) Mar 20 2012 That feature has been added to java with great benefit. I think the
- Andrej Mitrovic (4/7) Mar 19 2012 I'm really not talking about general-purpose attributes, I was
- F i L (3/4) Mar 19 2012 I meant that Attribute metadata is distinct data in the compilers
- Andrei Alexandrescu (3/34) Mar 19 2012 I salute creative uses of the language over defining new features.
- H. S. Teoh (6/12) Mar 19 2012 +1.
- Jacob Carlborg (5/7) Mar 19 2012 Sure, just as well as you can do object oriented programming in C, but
- dennis luehring (4/43) Mar 19 2012 and how to add attribute parameters like DoSerialize(type=packed) for
- Andrei Alexandrescu (3/6) Mar 20 2012 One more argument to the template?
- Jacob Carlborg (35/37) Mar 20 2012 The actual point of user defined attributes it to be able to create
- F i L (3/30) Mar 20 2012 That's pretty slick. D would need delegate inlining +
- Jacob Carlborg (5/7) Mar 20 2012 Yes, but currently, as far as I know, the delegate passed to opApply is
- Jacob Carlborg (4/32) Mar 19 2012 Yeah, but that will complicate the retrieval of the information.
- Andrej Mitrovic (44/45) Mar 19 2012 What is so complicated about extracting fields? Just iterate via .tupleo...
- Jacob Carlborg (5/8) Mar 20 2012 It wasn't actually that much more complicated. But now there is one
- deadalnix (13/31) Mar 20 2012 The attribute itself doesn't do anything at runtime. But the attribute
- James Miller (69/69) Mar 20 2012 Sorry for not quoting, but I'm not sure who to quote...
- Adam D. Ruppe (31/36) Mar 20 2012 I'm actually thinking of identifying them by type. Types
- Jacob Carlborg (6/21) Mar 21 2012 I think any type that a template can take (as a value) + other
- Tove (13/39) Mar 21 2012 With the mixin improvement proposal any arbitrarily complex
- Adam D. Ruppe (5/9) Mar 21 2012 My main concern with the library implementation isn't
- Artur Skawina (7/15) Mar 22 2012 A (new) compile-time only (mutable) storage class.
- Jacob Carlborg (5/8) Mar 25 2012 Accessing the custom attributes at runtime are necessary. Serialization
- Adam D. Ruppe (2/4) Mar 21 2012 Cool. I did not realize that.
- Jacob Carlborg (5/8) Mar 21 2012 Here's my proposal:
- Adam D. Ruppe (35/36) Mar 21 2012 Ah yes, I think I did read that before.
- Jacob Carlborg (9/38) Mar 21 2012 That would be nice.
- Steven Schveighoffer (28/36) Mar 21 2012 Really, the only requirement for an annotation expression should be that...
- Jacob Carlborg (4/10) Mar 21 2012 Does "@makeAPoint int x;" expand to "Point2d p; return p;"?
- Steven Schveighoffer (14/24) Mar 21 2012 What I envision is that @makeAPoint translates to the CTFE engine callin...
- Jacob Carlborg (5/11) Mar 21 2012 That sounds like a pretty good idea. Many good ideas have popped up in
- Manu (10/32) Mar 18 2012 I think that's a fair call. I wonder why Java feels the need for statefu...
- Paulo Pinto (8/11) Mar 19 2012 Except for shared types, since in C# data is shared by default, everythi...
- Adam D. Ruppe (24/24) Mar 16 2012 Another argument against would be "can we do
- Piotr Szturmaj (39/86) Mar 16 2012 This is just enum. If you use struct or class attributes (like in C#)
- Johannes Pfau (13/57) Mar 16 2012 +1
- Andrei Alexandrescu (15/21) Mar 16 2012 So we have:
- Adam D. Ruppe (42/44) Mar 16 2012 It can sort of work, but it isn't very good.
- Andrej Mitrovic (13/17) Mar 16 2012 Yeah, but I would say if we had even better compile-time introspection
- deadalnix (2/19) Mar 20 2012 And not just introspection, but modification would be king.
- Kapps (63/77) Mar 16 2012 This gets to an unreasonable amount of noise and complexity.
- Andrej Mitrovic (2/6) Mar 16 2012 That kind of turns D from a structural to a declarative language. :p
- Kapps (17/24) Mar 16 2012 Web design is quite a declarative thing. :) The code is already
- Manu (5/11) Mar 17 2012 And that's awesome in many situations! :)
- Gor Gyolchanyan (7/20) Mar 17 2012 What we're talking about here is subject-oriented programming: having
- deadalnix (19/41) Mar 20 2012 It is more tricky if the property isn't a simple attribute to read.
- Andrei Alexandrescu (6/10) Mar 20 2012 I dabbled into AOP quite a bit, but I'm not all that jazzed about it.
- deadalnix (14/24) Mar 20 2012 Honestly, AOP is quite hard to use right now because of the lack of
- Jacob Carlborg (4/22) Mar 21 2012 I couldn't agree more.
- Timon Gehr (2/4) Mar 21 2012 I'm sceptical. How would that work exactly?
- Andrei Alexandrescu (4/9) Mar 21 2012 I, too, am highly skeptical. For one thing these attributes must be made...
- deadalnix (6/16) Mar 21 2012 That is the point. The property must be able to manipulate what is
- Andrei Alexandrescu (9/27) Mar 21 2012 Problem is you'd need a ton of hooks to e.g. prevent mutation in const
-
Steven Schveighoffer
(12/17)
Mar 16 2012
I thought @
was supposed to be a user-defined annotation. - Adam D. Ruppe (11/14) Mar 16 2012 idk, to "reduce" the number of keywords or somethiny.
- Steven Schveighoffer (16/26) Mar 16 2012 Quote from TDPL (section 5.9.1 on page 156):
- Adam D. Ruppe (17/20) Mar 16 2012 Huh. There's nothing I can see in the compiler that even
- Steven Schveighoffer (21/36) Mar 16 2012 Right, the user-defined portion is not implemented.
- deadalnix (4/17) Mar 20 2012 This isn't a problem because they'd be scoped to the module anyway. D
- Alvaro (24/28) Mar 16 2012 I like that.
- Jacob Carlborg (41/44) Mar 17 2012 I would love to have user defined attributes in D but I'm not completely...
- bearophile (13/16) Mar 18 2012 Maybe there are other ways to think about user defined
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
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 counterarguments 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
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 myAttributeI 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
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:Interesting approach, how will that affect 'thing's type?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 'customattributes' .. 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 myAttributeI 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
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
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: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...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
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
On 17 March 2012 21:56, Walter Bright <newshound2 digitalmars.com> wrote:On 3/16/2012 7:11 AM, Manu wrote: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 haveattribute 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
On 17 March 2012 23:52, Manu <turkeyman gmail.com> wrote:On 17 March 2012 21:56, Walter Bright <newshound2 digitalmars.com> 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.On 3/16/2012 7:11 AM, Manu wrote: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 haveattribute 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
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
On 18 March 2012 03:10, Walter Bright <newshound2 digitalmars.com> wrote:On 3/17/2012 2:55 PM, Manu wrote:After initialisation, before construction. 2. sharedI 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/destructionHow is this any different than for the class its self? 3. const/immutableInherit these properties. 4. thread localitySame as class. 5. allocated statically or dynamicallyI'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
On 3/17/2012 6:39 PM, Manu wrote:of issues associated.
Mar 17 2012
On 18 March 2012 03:48, Walter Bright <newshound2 digitalmars.com> wrote:On 3/17/2012 6:39 PM, Manu wrote: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?same set of issues associated.types.
Mar 17 2012
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
On Sunday, 18 March 2012 at 01:48:07 UTC, Walter Bright wrote:On 3/17/2012 6:39 PM, Manu wrote: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.precisely the same set of issues associated.threadlocal/shared types.
Mar 17 2012
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
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
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
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
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
On 3/17/2012 9:12 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 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.
Mar 17 2012
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
On 3/17/2012 10:01 PM, F i L wrote:Walter Bright wrote:I mean there is modifiable-at-runtime, instance-specific data.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),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
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" }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, IIn 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.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.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.
Mar 18 2012
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
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
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: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.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
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
On Sunday, 18 March 2012 at 10:50:14 UTC, F i L 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 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.).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
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
On Sunday, 18 March 2012 at 12:10:23 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; });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
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
On Sunday, 18 March 2012 at 12:39:19 UTC, F i L wrote:Tove wrote: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.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
On 3/18/2012 2:47 AM, F i L wrote:Walter Bright wrote:Which looks indistinguishable from modifiable at runtime, instance specific data.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" }Sorry, it still looks like standard lazy initialization. I don't know what attributes add to the party.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 theSimply use a normal class. Instantiate it at runtime as needed.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.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
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
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
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
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
On Monday, 19 March 2012 at 06:46:09 UTC, dennis luehring wrote:Am 19.03.2012 01:41, schrieb Walter Bright: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... } ");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 } }
Mar 19 2012
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 orientatedGC.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
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 orientatedclasses) 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
Am 19.03.2012 10:03, schrieb F i L:dennis luehring wrote:sound to simple to fit in non trival casesi 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 orientatedclasses) 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
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
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
On 2012-03-19 01:41, Walter Bright wrote:On 3/18/2012 2:58 PM, Jacob Carlborg wrote: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.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?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
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
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: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 areWalter Bright wrote:I mean there is modifiable-at-runtime, instance-specific data.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?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
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: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 areWalter Bright wrote:I mean there is modifiable-at-runtime, instance-specific data.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?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
On 2012-03-19 13:31, Steven Schveighoffer wrote:On Sun, 18 Mar 2012 04:03:29 -0400, Walter BrightI agree.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 whatFor 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.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. -SteveVery well explained. -- /Jacob Carlborg
Mar 19 2012
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
On Monday, 19 March 2012 at 20:44:43 UTC, Andrej Mitrovic wrote:On 3/19/12, Jacob Carlborg <doob me.com> 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?* 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
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
On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:On 3/19/12 3:55 PM, F i L wrote:So the compiler would always search for enums with a specific "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" signature? Guess that could work.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
On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:On 3/19/12 3:55 PM, F i L wrote: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.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
On 3/20/12 12:50 AM, Kapps wrote:On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:Perhaps we should add a field of type Variant[string].On 3/19/12 3:55 PM, F i L wrote: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.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. AndreiThis 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
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.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 CarlborgThis 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.
Mar 20 2012
On 3/20/12 10:52 AM, Jacob Carlborg wrote:On 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.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. AndreiI'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
Mar 20 2012
Andrei Alexandrescu wrote:On 3/20/12 10:52 AM, Jacob Carlborg wrote:Please, do not transform D into a dynamic language.On 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 20 2012
On 3/20/12 2:04 PM, Piotr Szturmaj wrote:Andrei Alexandrescu wrote:Where's the "dynamic languages rok" crowd when you need it :o). AndreiOn 3/20/12 10:52 AM, Jacob Carlborg wrote:Please, do not transform D into a dynamic language.On 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 20 2012
Le 20/03/2012 20:17, Andrei Alexandrescu a écrit :On 3/20/12 2:04 PM, Piotr Szturmaj wrote:They are busy debugging what they coded sooooooo quickly and efficiently.Andrei Alexandrescu wrote:Where's the "dynamic languages rok" crowd when you need it :o). AndreiOn 3/20/12 10:52 AM, Jacob Carlborg wrote:Please, do not transform D into a dynamic language.On 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 20 2012
On 2012-03-20 20:17, Andrei Alexandrescu wrote:On 3/20/12 2:04 PM, Piotr Szturmaj wrote:They left you behind after all those "d rox" comments :) -- /Jacob CarlborgPlease, do not transform D into a dynamic language.Where's the "dynamic languages rok" crowd when you need it :o). Andrei
Mar 20 2012
Le 20/03/2012 17:13, Andrei Alexandrescu a écrit :On 3/20/12 10:52 AM, Jacob Carlborg wrote:I was probably one of the most controversial part of the thread :DOn 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 20 2012
On 2012-03-20 17:13, Andrei Alexandrescu wrote:On 3/20/12 10:52 AM, Jacob Carlborg wrote:Maybe you agreed on that. I still don't like it. -- /Jacob CarlborgOn 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 20 2012
On 20/03/12 22:29, Jacob Carlborg wrote:On 2012-03-20 17:13, Andrei Alexandrescu wrote: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.On 3/20/12 10:52 AM, Jacob Carlborg wrote:Maybe you agreed on that. I still don't like it.On 2012-03-20 16:17, Andrei Alexandrescu wrote:Why? I thought it was agreed that that's a good idea for exceptions.On 3/20/12 12:50 AM, Kapps wrote:Perhaps we should add a field of type Variant[string].No, not Variant[string] again.
Mar 22 2012
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
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.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.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 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
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:This is trivializing my point. Please.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.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?I hope I'll be convinced. AndreiAlso, 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.
Mar 20 2012
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
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: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.On Tue, 20 Mar 2012 11:17:02 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:This is trivializing my point. Please.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.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.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.I haven't seen that. Perhaps a link?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 think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -SteveNote 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?
Mar 21 2012
On 2012-03-21 14:54, Steven Schveighoffer wrote:On Tue, 20 Mar 2012 12:16:41 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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 CarlborgMaybe 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
On 3/21/12 9:57 AM, Jacob Carlborg wrote:On 2012-03-21 14:54, Steven Schveighoffer 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.On Tue, 20 Mar 2012 12:16:41 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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); }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. -Steveserialize(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
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
On Wednesday, 21 March 2012 at 15:20:35 UTC, F i L wrote:Andrei Alexandrescu 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"];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
static const __nonSerialized = ["int":"a", "int":"b"];Typo, I meant: ["int":"a", "float":"b"];
Mar 21 2012
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:Is this implemented, or a thought? AndreiAndrei Alexandrescu 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"];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
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
On 3/21/12 11:15 AM, F i L wrote:Andrei Alexandrescu wrote:Wouldn't a better approach rely on a compile-time structure instead of a hash? AndreiIs 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
On Wednesday, 21 March 2012 at 16:21:21 UTC, Andrei Alexandrescu wrote:On 3/21/12 11:15 AM, 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.Andrei Alexandrescu wrote:Wouldn't a better approach rely on a compile-time structure instead of a hash?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
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
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
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
On 2012-03-21 18:22, F i L wrote:Jacob Carlborg wrote:I'm using a static const variable because it's compatible with D1. It still works at compile time. -- /Jacob CarlborgOnly 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
On 3/21/12 10:20 AM, F i L wrote:Andrei Alexandrescu wrote: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. AndreiIn 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
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
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
On 2012-03-21 16:20, F i L wrote:Andrei Alexandrescu wrote:Exactly. -- /Jacob CarlborgIn 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
On 2012-03-21 16:11, Andrei Alexandrescu wrote: I think the liability here is that b needs to appear in two places, oncein 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
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, onceWell if the argument boils down to nice vs. ugly, as opposed to possible vs. impossible - it's quite a bit less compelling. Andreiin 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.
Mar 21 2012
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
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
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
On 3/22/12 2:32 AM, Andrei Alexandrescu wrote:On 3/21/12 12:06 PM, Jacob Carlborg wrote: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...On 2012-03-21 16:11, Andrei Alexandrescu wrote: I think the liability here is that b needs to appear in two places, onceWell if the argument boils down to nice vs. ugly, as opposed to possible vs. impossible - it's quite a bit less compelling. Andreiin 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.
Mar 22 2012
On Wednesday, 21 March 2012 at 15:11:47 UTC, Andrei Alexandrescu wrote: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); }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.
Mar 21 2012
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
On Sunday, 25 March 2012 at 15:24:18 UTC, Jacob Carlborg wrote:On 2012-03-22 02:23, Tove wrote: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"...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.
Mar 25 2012
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
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Now, this is pure beauty. :)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).
Mar 22 2012
On Thursday, 22 March 2012 at 10:18:24 UTC, Andrej Mitrovic wrote:On 3/21/12, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote: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.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).
Mar 22 2012
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
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
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 think the closest anyone has come is Jacob, with his orange library. Maybe he can respond to this point. -SteveNote 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?
Mar 27 2012
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
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:Forgot to mention - I snipped these points because I agree with them. AndreiPerhaps 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.
Mar 20 2012
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
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
Le 20/03/2012 16:17, Andrei Alexandrescu a écrit :On 3/20/12 12:50 AM, Kapps wrote: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.On Monday, 19 March 2012 at 21:00:32 UTC, Andrei Alexandrescu wrote:Perhaps we should add a field of type Variant[string].On 3/19/12 3:55 PM, F i L wrote: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.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. AndreiThis 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.
Mar 20 2012
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
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
On 3/19/12 3:44 PM, Andrej Mitrovic wrote:On 3/19/12, Jacob Carlborg<doob me.com> wrote:I salute creative uses of the language over defining new features. Andrei* 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
On Mon, Mar 19, 2012 at 04:00:00PM -0500, Andrei Alexandrescu wrote:On 3/19/12 3:44 PM, Andrej Mitrovic wrote:[...]+1. T -- Life is too short to run proprietary software. -- Bdale GarbeeSo 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.
Mar 19 2012
On 2012-03-19 22:00, Andrei Alexandrescu wrote:I salute creative uses of the language over defining new features. AndreiSure, 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
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:I salute creative uses of the language over defining new features. Andrei* 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
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_annotationOne more argument to the template? Andrei
Mar 20 2012
On 2012-03-19 22:00, Andrei Alexandrescu wrote:I salute creative uses of the language over defining new features. AndreiThe 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
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
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
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
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
On 2012-03-19 22:51, Andrej Mitrovic wrote:On 3/19/12, Jacob Carlborg<doob me.com> wrote:It wasn't actually that much more complicated. But now there is one extra enum for each NonSerialized field. -- /Jacob CarlborgYeah, but that will complicate the retrieval of the information.What is so complicated about extracting fields? Just iterate via .tupleof:
Mar 20 2012
Le 18/03/2012 05:49, Walter Bright a écrit :On 3/17/2012 9:12 PM, F i L wrote: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.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 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.
Mar 20 2012
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
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
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.I think any type that a template can take (as a value) + other attributes/annotations. -- /Jacob CarlborgWhat data can be packed into annotations?Being able to use custom types is an important part of my idea here.
Mar 21 2012
On Wednesday, 21 March 2012 at 08:08:12 UTC, Jacob Carlborg wrote:On 2012-03-21 01:35, Adam D. Ruppe 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. #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...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.I think any type that a template can take (as a value) + other attributes/annotations.What data can be packed into annotations?Being able to use custom types is an important part of my idea here.
Mar 21 2012
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
On 03/21/12 14:36, Adam D. Ruppe wrote:On Wednesday, 21 March 2012 at 08:29:23 UTC, Tove wrote: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. arturWith 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 22 2012
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. arturAccessing the custom attributes at runtime are necessary. Serialization is one example that could take advantage of that. -- /Jacob Carlborg
Mar 25 2012
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
On 2012-03-21 15:02, Adam D. Ruppe wrote:On Wednesday, 21 March 2012 at 08:08:12 UTC, Jacob Carlborg wrote:Here's my proposal: http://forum.dlang.org/thread/bccwycoexxykfgxvedix forum.dlang.org?page=3#post-jk2d4t:242ban:241:40digitalmars.com -- /Jacob CarlborgThat's basically my initial proposal and how annotations work in Java.Cool. I did not realize that.
Mar 21 2012
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
On 2012-03-21 17:00, Adam D. Ruppe wrote:On Wednesday, 21 March 2012 at 15:02:15 UTC, Jacob Carlborg wrote:Would "key: value" be possible. The syntax we have for AA literals?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);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
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: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. -SteveThe 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.
Mar 21 2012
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
On Wed, 21 Mar 2012 17:10:06 -0400, Jacob Carlborg <doob me.com> wrote:On 2012-03-21 19: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". 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. -SteveIn 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;"?
Mar 21 2012
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
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: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.On 3/17/2012 6:39 PM, Manu wrote: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.same set of issues associated.types.
Mar 18 2012
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
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
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
Am Fri, 16 Mar 2012 16:21:41 +0100 schrieb Piotr Szturmaj <bncrbme jadamspam.pl>:+1note(Serializable.yes) int a;then direct Struct(constructor args..) might be better.+1The 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).Or we could add a attribute attribute: attribute struct Test { this(int a) {}... } Test(99) string someTestVal;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.And all normal import rules should work for user defined attributes as well (renamed imports, static imports, ...).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("").
Mar 16 2012
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
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
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
Le 16/03/2012 19:23, Andrej Mitrovic a écrit :On 3/16/12, Adam D. Ruppe<destructionator gmail.com> wrote:And not just introspection, but modification would be king.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 20 2012
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. AndreiThis 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
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
On Saturday, 17 March 2012 at 00:54:02 UTC, Andrej Mitrovic wrote:On 3/17/12, Kapps <opantm2+spam gmail.com> wrote: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.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
On 17 March 2012 02:53, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:And that's awesome in many situations! :) this, and it's an invaluable language feature. It adds so much simplicity to the tools code.WebForm("Account"); PostTo("Services/CreateAccount") SecureOnly(true) struct CreateAccountForm {That kind of turns D from a structural to a declarative language. :p
Mar 17 2012
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:-- Bye, Gor Gyolchanyan.On 3/17/12, Kapps <opantm2+spam gmail.com> wrote:And that's awesome in many situations! :) this, and it's an invaluable language feature. It adds so much simplicity to the tools code.WebForm("Account"); PostTo("Services/CreateAccount") SecureOnly(true) struct CreateAccountForm {That kind of turns D from a structural to a declarative language. :p
Mar 17 2012
Le 16/03/2012 17:09, Andrei Alexandrescu a écrit :On 3/16/12 8:35 AM, Adam D. Ruppe 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.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 20 2012
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
Le 20/03/2012 22:36, Andrei Alexandrescu a écrit :On 3/20/12 4:36 PM, deadalnix wrote: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 ».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
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
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
On 3/21/12 11:17 AM, Timon Gehr wrote:On 03/20/2012 10:36 PM, deadalnix wrote:I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. AndreiEven 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
Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :On 3/21/12 11:17 AM, Timon Gehr wrote: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 ?On 03/20/2012 10:36 PM, deadalnix wrote:I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. AndreiEven 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
On 3/21/12 6:02 PM, deadalnix wrote:Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :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.On 3/21/12 11:17 AM, Timon Gehr wrote:That is the point. The property must be able to manipulate what is qualified. This is the point of AOP.On 03/20/2012 10:36 PM, deadalnix wrote:I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. AndreiEven 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?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
Le 22/03/2012 03:05, Andrei Alexandrescu a écrit :On 3/21/12 6:02 PM, deadalnix wrote: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.Le 21/03/2012 17:22, Andrei Alexandrescu a écrit :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.On 3/21/12 11:17 AM, Timon Gehr wrote:That is the point. The property must be able to manipulate what is qualified. This is the point of AOP.On 03/20/2012 10:36 PM, deadalnix wrote:I, too, am highly skeptical. For one thing these attributes must be made part of the type and have deep connections with code semantics. AndreiEven 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?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.Yes.Have you seen what a project like lombok can do ?This? http://projectlombok.org/ I'll take a look, thanks.
Mar 22 2012
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
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
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
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: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/DIP6I 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.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
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
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:Right, the user-defined portion is not implemented.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.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.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 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
Le 16/03/2012 18:36, Adam D. Ruppe a écrit :On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer wrote: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 !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 20 2012
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
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
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