digitalmars.D - Defining some stuff for each class in turn
- Andrei Alexandrescu (51/51) Oct 01 2009 I am becoming increasingly aware that we need to provide some means to
- Jarrett Billingsley (18/67) Oct 01 2009 e
- Andrei Alexandrescu (49/58) Oct 01 2009 I've known for a long time this was in store if we want to define decent...
- grauzone (4/14) Oct 01 2009 I don't see how that would be needed for reflection, and I've written
- Phil Deets (6/18) Oct 01 2009 I like that you can use this in interfaces too. It seems to allow for
- Andrei Alexandrescu (7/29) Oct 01 2009 That's a great point. To compare this feature with Scala's mixins (which...
- Steven Schveighoffer (35/91) Oct 02 2009 This still isn't optimal. For example, two different derivatives of the...
- Max Samukha (9/12) Oct 01 2009 I am glad you have finally bumped into the problem. It has been
- Michel Fortin (9/13) Oct 01 2009 I'm using the exact same solution here for the D/Objective-C bridge and
- Leandro Lucarella (19/20) Oct 01 2009 I think this is close to Python metaclasses[1], but much more specific.
- Denis Koroskin (46/97) Oct 01 2009 This is cool. I'd also add Serialization task to the application list
- Andrei Alexandrescu (14/73) Oct 01 2009 Yah, serialization is close to reflection so it's definitely part of the...
- Christopher Wright (7/10) Oct 01 2009 Most of the examples given would be well served by decent builtin
- Andrei Alexandrescu (7/18) Oct 01 2009 What cheaper way would be than allowing a base class to prescribe
- Christopher Wright (15/35) Oct 02 2009 Once you get two or three libraries using reflection, it's cheaper to
I am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there. Right now that right is reserved to the compiler, which generates e.g. one static classinfo object for each class. But users would want to also define members for each class automatically. This is often the case with contravariant-argument functions (that we discussed recently) and other important cases such as factories and cloning. For starters, assume I want to define one static int for each class in a hierarchy. class Counted { static uint counter; ... } Then subclasses of Counted would all share counter, something I don't want. I want each subclass to have its own counter, so I need to ask derived classes to *also* define counter: class A : Counted { static uint counter; ... } With the suggested feature, there would be the ability to define counter for each class in turn. class Counted { mixin(Derived) { // Insert here stuff that must be "pasted" for each subclass // of Counted (including Counted itself). // Use "Derived" as the name of the current subtype of Counter static uint counter; static if (is(Counted == Derived)) ref uint getCounter() { return counter; } else override ref uint getCounter() { return counter; } } ... } The code above does something quite neat - it defines an overridable function Counted.getCounter in the base class, and then overrides it in *every* class inheriting that base class, to return that class' own counter. The same should go in interface definitions - the feature should allow you to There are quite a few immediate applications of this: opEquals, opCmp, clone, various factories and object pools, and most importantly reflection. To enable custom reflection with today's D, we'd have to require each class to insert some code inside the class body. With the mechanism described above, we allow the base class or an interface (e.g. Reflectable) to inject the code into the derived class' body. What do you think? Andrei
Oct 01 2009
On Thu, Oct 1, 2009 at 12:25 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they wer=epasted there. Right now that right is reserved to the compiler, which generates e.g. on=estatic classinfo object for each class. But users would want to also defi=nemembers for each class automatically. This is often the case with contravariant-argument functions (that we discussed recently) and other important cases such as factories and cloning. For starters, assume I want to define one static int for each class in a hierarchy. class Counted { =A0 =A0static uint counter; =A0 =A0... } Then subclasses of Counted would all share counter, something I don't wan=t.I want each subclass to have its own counter, so I need to ask derived classes to *also* define counter: class A : Counted { =A0 =A0static uint counter; =A0 =A0... } With the suggested feature, there would be the ability to define counter =A0for each class in turn. class Counted { =A0 =A0mixin(Derived) =A0 =A0{ =A0 =A0 =A0 =A0// Insert here stuff that must be "pasted" for each subcla=ss=A0 =A0 =A0 =A0// of Counted (including Counted itself). =A0 =A0 =A0 =A0// Use "Derived" as the name of the current subtype of Cou=nter=A0 =A0 =A0 =A0static uint counter; =A0 =A0 =A0 =A0static if (is(Counted =3D=3D Derived)) =A0 =A0 =A0 =A0 =A0 =A0ref uint getCounter() { return counter; } =A0 =A0 =A0 =A0else =A0 =A0 =A0 =A0 =A0 =A0override ref uint getCounter() { return counter; } =A0 =A0} =A0 =A0... } The code above does something quite neat - it defines an overridable function Counted.getCounter in the base class, and then overrides it in *every* class inheriting that base class, to return that class' own count=er.The same should go in interface definitions - the feature should allow yo=uto There are quite a few immediate applications of this: opEquals, opCmp, clone, various factories and object pools, and most importantly reflectio=n.To enable custom reflection with today's D, we'd have to require each cla=ssto insert some code inside the class body. With the mechanism described above, we allow the base class or an interface (e.g. Reflectable) to inje=ctthe code into the derived class' body. What do you think?I think it sounds interesting enough, but I can't help but wonder if this is a feature that you've really thought through (especially wrt. how it interacts with mechanisms such as template mixins and normal symbol inheritance), or if you just want it to support some pattern you want to use in Phobos 2.
Oct 01 2009
Jarrett Billingsley wrote:On Thu, Oct 1, 2009 at 12:25 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[code injection]I've known for a long time this was in store if we want to define decent reflection. It's also been a perennial source of trouble with a lot of code that needs to inject members. Let me give another example. When we discussed opCmp around here, people said that's the old way of doing things and that the right way is to use an interface Comparator!T, akin to Java's Comparator<T>: http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html That only solves exactly one level of inheritance. It's a more elaborate solution that doesn't quite help that much. Consider: interface Comparator(T) { int opCmp(T rhs); // yum, exact type } class Widget : Comparator!Widget { override opCmp(Widget rhs) { ... } // yum, exact type } class Gadget : Widget { override opCmp(Gadget rhs) { ... } // ERROR!!!!!!!! } Of course that didn't work. Gadget.opCmp doesn't override anything. Gadget needs to remember to inherit Comparator!Gadget: class Gadget : Widget, Comparator!Gadget { override opCmp(Gadget rhs) { ... } // oh ok } So any class X in Widget's hierarchy that forgets to inherit Comparator!X undergoes the risk of having the wrong opCmp called for it. With injection, Comparator can be made to work for any interface: interface Comparator(T) { int opCmp(Comparator!T rhs); mixin(Impl) // for each implementation Impl { int opCmp(Impl rhs); override int opCmp(Comparator!T rhs) { return opCmp(cast(Impl) rhs); } } } class Widget : Comparator!Widget { ... } Now every derived class of Widget, including Widget itself, commits to define a opCmp with the proper signature (failure to do so results in a linker error), and also defines the interface function in terms of it. AndreiWhat do you think?I think it sounds interesting enough, but I can't help but wonder if this is a feature that you've really thought through (especially wrt. how it interacts with mechanisms such as template mixins and normal symbol inheritance), or if you just want it to support some pattern you want to use in Phobos 2.
Oct 01 2009
Andrei Alexandrescu wrote:Jarrett Billingsley wrote:I don't see how that would be needed for reflection, and I've written some custom reflection/serialization myself. Yay for tupleof being able to read even private members.I think it sounds interesting enough, but I can't help but wonder if this is a feature that you've really thought through (especially wrt. how it interacts with mechanisms such as template mixins and normal symbol inheritance), or if you just want it to support some pattern you want to use in Phobos 2.I've known for a long time this was in store if we want to define decent reflection. It's also been a perennial source of trouble with a lot of code that needs to inject members.
Oct 01 2009
On Thu, 01 Oct 2009 12:53:46 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:interface Comparator(T) { int opCmp(Comparator!T rhs); mixin(Impl) // for each implementation Impl { int opCmp(Impl rhs); override int opCmp(Comparator!T rhs) { return opCmp(cast(Impl) rhs); } } }I like that you can use this in interfaces too. It seems to allow for something similar to multiple inheritance, but presumably without the implementation difficulty. Phil Deets
Oct 01 2009
Phil Deets wrote:On Thu, 01 Oct 2009 12:53:46 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:That's a great point. To compare this feature with Scala's mixins (which I studied and now feel are a good feature), my understanding is that Scala mixins are an interface plus a default implementation. The feature proposed above is an interface with a prescribed implementation. That way, Scala mixins seem to be somewhat more flexible. Andreiinterface Comparator(T) { int opCmp(Comparator!T rhs); mixin(Impl) // for each implementation Impl { int opCmp(Impl rhs); override int opCmp(Comparator!T rhs) { return opCmp(cast(Impl) rhs); } } }I like that you can use this in interfaces too. It seems to allow for something similar to multiple inheritance, but presumably without the implementation difficulty. Phil Deets
Oct 01 2009
On Thu, 01 Oct 2009 13:53:46 -0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Jarrett Billingsley wrote:This still isn't optimal. For example, two different derivatives of the same class could try to compare to eachother and end up passing null into your opCmp. It would be nice if the compiler could reject unrelated comparisons: class X: Comparator!X { int opCmp(X) {} } class Y: X { int opCmp(Y) {} } class Z: X { int opCmp(Z) {} } void main() { auto y = new Y; auto z = new Z; z < y; // error, rejected because they can't possibly be related } If there was a way to do that it would be cool. I also think the auto-defined opCmp should look like this: override int opCmp(Comparator!T rhs) { if(auto imp = cast(Impl) rhs) return opCmp(imp); return false; } That would cut down on calls into your actual opCmp function with a null argument. -SteveOn Thu, Oct 1, 2009 at 12:25 PM, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[code injection]I've known for a long time this was in store if we want to define decent reflection. It's also been a perennial source of trouble with a lot of code that needs to inject members. Let me give another example. When we discussed opCmp around here, people said that's the old way of doing things and that the right way is to use an interface Comparator!T, akin to Java's Comparator<T>: http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html That only solves exactly one level of inheritance. It's a more elaborate solution that doesn't quite help that much. Consider: interface Comparator(T) { int opCmp(T rhs); // yum, exact type } class Widget : Comparator!Widget { override opCmp(Widget rhs) { ... } // yum, exact type } class Gadget : Widget { override opCmp(Gadget rhs) { ... } // ERROR!!!!!!!! } Of course that didn't work. Gadget.opCmp doesn't override anything. Gadget needs to remember to inherit Comparator!Gadget: class Gadget : Widget, Comparator!Gadget { override opCmp(Gadget rhs) { ... } // oh ok } So any class X in Widget's hierarchy that forgets to inherit Comparator!X undergoes the risk of having the wrong opCmp called for it. With injection, Comparator can be made to work for any interface: interface Comparator(T) { int opCmp(Comparator!T rhs); mixin(Impl) // for each implementation Impl { int opCmp(Impl rhs); override int opCmp(Comparator!T rhs) { return opCmp(cast(Impl) rhs); } } } class Widget : Comparator!Widget { ... } Now every derived class of Widget, including Widget itself, commits to define a opCmp with the proper signature (failure to do so results in a linker error), and also defines the interface function in terms of it.What do you think?I think it sounds interesting enough, but I can't help but wonder if this is a feature that you've really thought through (especially wrt. how it interacts with mechanisms such as template mixins and normal symbol inheritance), or if you just want it to support some pattern you want to use in Phobos 2.
Oct 02 2009
On Thu, 01 Oct 2009 11:25:03 -0500, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there.I am glad you have finally bumped into the problem. It has been waiting for a solution for quite some time. For example, the upcoming reflection mechanism in QtD (which uses its own metaobjects instead of the limited classinfo) requires manual insertion of a mixin into derived classes unless the mixin is inserted implicitly when a signal/property/etc is declared. A couple of votes for the feature.
Oct 01 2009
On 2009-10-01 14:05:10 -0400, Max Samukha <spambox d-coding.com> said:For example, the upcoming reflection mechanism in QtD (which uses its own metaobjects instead of the limited classinfo) requires manual insertion of a mixin into derived classes unless the mixin is inserted implicitly when a signal/property/etc is declared.I'm using the exact same solution here for the D/Objective-C bridge and it's working surprisingly well. I certainly wouldn't turn down the possibility of inheriting an implementation in subclasses, it'd make things a little less hackish. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Oct 01 2009
Andrei Alexandrescu, el 1 de octubre a las 11:25 me escribiste:What do you think?I think this is close to Python metaclasses[1], but much more specific. It's amazing how many cool things one can do with Python metaclasses (there are several articles discussing them[2]) and decorators. I would love to see something similar in D but operating at compile-time. I think it would be nice to point to a more general solution (like AST macros) than implementing all sort of small and specific tricks for each problem instead. [1] http://docs.python.org/reference/datamodel.html#customizing-class-creation [2] http://en.wikipedia.org/wiki/Metaclass http://www.ibm.com/developerworks/linux/library/l-pymeta.html -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Wake from your sleep, the drying of your tears, Today we escape, we escape.
Oct 01 2009
On Thu, 01 Oct 2009 20:25:03 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:I am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there. Right now that right is reserved to the compiler, which generates e.g. one static classinfo object for each class. But users would want to also define members for each class automatically. This is often the case with contravariant-argument functions (that we discussed recently) and other important cases such as factories and cloning. For starters, assume I want to define one static int for each class in a hierarchy. class Counted { static uint counter; ... } Then subclasses of Counted would all share counter, something I don't want. I want each subclass to have its own counter, so I need to ask derived classes to *also* define counter: class A : Counted { static uint counter; ... } With the suggested feature, there would be the ability to define counter for each class in turn. class Counted { mixin(Derived) { // Insert here stuff that must be "pasted" for each subclass // of Counted (including Counted itself). // Use "Derived" as the name of the current subtype of Counter static uint counter; static if (is(Counted == Derived)) ref uint getCounter() { return counter; } else override ref uint getCounter() { return counter; } } ... } The code above does something quite neat - it defines an overridable function Counted.getCounter in the base class, and then overrides it in *every* class inheriting that base class, to return that class' own counter. The same should go in interface definitions - the feature should allow you to There are quite a few immediate applications of this: opEquals, opCmp, clone, various factories and object pools, and most importantly reflection. To enable custom reflection with today's D, we'd have to require each class to insert some code inside the class body. With the mechanism described above, we allow the base class or an interface (e.g. Reflectable) to inject the code into the derived class' body. What do you think? AndreiThis is cool. I'd also add Serialization task to the application list (currently I manually insert "mixin Serializable!();" into every class. There is one thing that I dislike about it, though. I believe mixin(Foo) {} syntax is a bit far-fetched, it is not obvious that Foo represents derived type inside a mixin scope. I'd like to remind you that we could just use typeof(this) instead of Foo/Derived/etc: static if (is (typeof(this) == Counted) { ref uint getCounter() { return counter; } } else { override ref uint getCounter() { return counter; } } If you remember, we had a similar discussion about a half year ago. It was about ICloneable interface that forces all derived class to implement proper clone() method. We could define it as: // forces implementor class and all the derivatives to implement proper clone() method interface ICloneable { scope mixin { // alternative syntax typeof(this) clone(); } } and also be able to declare a method non-recursively (so that derived class don't have to implement that method): interface ICloneableNonRecursive { typeof(this) clone(); } Other possible solution could be to use a special "derived" keyword. We already have a class-level compile-time constant - "super". We could also introduce its counter-part ("derived"), which will expand into a concrete derived class type. This will simplify the syntax a bit and will allow to get rid of mixin and an addition level of indentation: interface ICloneable { derived clone(); } interface IComparable { int opCmp(derived other); } As a downside, I don't know how to define a field in every class this way ("int derived.counter;" maybe?).
Oct 01 2009
Denis Koroskin wrote:On Thu, 01 Oct 2009 20:25:03 +0400, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:[...]Yah, serialization is close to reflection so it's definitely part of the target applications of the features.What do you think? AndreiThis is cool. I'd also add Serialization task to the application list (currently I manually insert "mixin Serializable!();" into every class.There is one thing that I dislike about it, though. I believe mixin(Foo) {} syntax is a bit far-fetched, it is not obvious that Foo represents derived type inside a mixin scope. I'd like to remind you that we could just use typeof(this) instead of Foo/Derived/etc: static if (is (typeof(this) == Counted) { ref uint getCounter() { return counter; } } else { override ref uint getCounter() { return counter; } }Yah, I thought a lot along the likes of typeof(this). We need some symbolic alias for "the struct or class currently being defined" and typeof(this) is an obvious choice. I just fear that it might be confusing that typeof(this) works even outside any method, i.e. when there is no "this".If you remember, we had a similar discussion about a half year ago. It was about ICloneable interface that forces all derived class to implement proper clone() method. We could define it as: // forces implementor class and all the derivatives to implement proper clone() method interface ICloneable { scope mixin { // alternative syntax typeof(this) clone(); } }Yah, I remember.and also be able to declare a method non-recursively (so that derived class don't have to implement that method): interface ICloneableNonRecursive { typeof(this) clone(); } Other possible solution could be to use a special "derived" keyword. We already have a class-level compile-time constant - "super". We could also introduce its counter-part ("derived"), which will expand into a concrete derived class type. This will simplify the syntax a bit and will allow to get rid of mixin and an addition level of indentation: interface ICloneable { derived clone(); } interface IComparable { int opCmp(derived other); } As a downside, I don't know how to define a field in every class this way ("int derived.counter;" maybe?).I dislike adding yet another keyword. In fact, I want to propose Walter to eliminate "super" as a keyword and make it just an alias for the base class as if you defined it by hand. Andrei
Oct 01 2009
Andrei Alexandrescu wrote:I am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there.Most of the examples given would be well served by decent builtin reflection. Walter thinks reflection is too expensive to be active by default. Find a cheaper way to provide runtime reflection. It's not as sexy as using templates, but it's DRY and easier to use. Slower, but you don't pay the cost of reflection multiple times if you have multiple libraries requiring reflection.
Oct 01 2009
Christopher Wright wrote:Andrei Alexandrescu wrote:What cheaper way would be than allowing a base class to prescribe reflection for its hierarchy? Where do templates even enter the mix? What's slower and why? Why do reflection as a language feature (increases base language size, buggy, rigid) instead of allowing it as a library if we so can? I'm totally against that. AndreiI am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there.Most of the examples given would be well served by decent builtin reflection. Walter thinks reflection is too expensive to be active by default. Find a cheaper way to provide runtime reflection. It's not as sexy as using templates, but it's DRY and easier to use. Slower, but you don't pay the cost of reflection multiple times if you have multiple libraries requiring reflection.
Oct 01 2009
Andrei Alexandrescu wrote:Christopher Wright wrote:Once you get two or three libraries using reflection, it's cheaper to have the language provide it than to have separate, incompatible reflection systems. It doesn't matter whether the language provides reflection or the standard library. (The only way for me to tell is whether I'm importing from core or from std.) Either way, it should be standardized and easy to opt in or out (depending on the default), and there should be no way or no cost for opting in twice. You have to use templates to get reflection info, currently. Unless CTFE has gotten a lot better, in which case it's effectively the same as using templates, but with a bit less executable bloat. Template-based solutions can be faster than reflection-based solutions because you can access fields and methods directly. But runtime reflection doesn't disallow templates.Andrei Alexandrescu wrote:What cheaper way would be than allowing a base class to prescribe reflection for its hierarchy? Where do templates even enter the mix? What's slower and why? Why do reflection as a language feature (increases base language size, buggy, rigid) instead of allowing it as a library if we so can? I'm totally against that. AndreiI am becoming increasingly aware that we need to provide some means to define certain members (data and functions) for each class as if they were pasted there.Most of the examples given would be well served by decent builtin reflection. Walter thinks reflection is too expensive to be active by default. Find a cheaper way to provide runtime reflection. It's not as sexy as using templates, but it's DRY and easier to use. Slower, but you don't pay the cost of reflection multiple times if you have multiple libraries requiring reflection.
Oct 02 2009