digitalmars.D - Syntactic Sugar for Virtual Constructors?
- Janice Caron (45/45) Feb 28 2008 Something Andrei said got me thinking.
- Steven Schveighoffer (14/60) Feb 28 2008 what about static opCall?
- Janice Caron (14/15) Feb 28 2008 Ooh - I never thought of that. That's close.
- BCS (16/38) Feb 28 2008 how about allow this functions in abstract classes
- Janice Caron (15/25) Feb 28 2008 The switch is the one thing that you /must not have/. The factory
- BCS (24/56) Feb 28 2008 your point is valid (in some cases) but totally orthogonal to mine. Re
- Christopher Wright (20/47) Feb 28 2008 A better way:
- Christopher Wright (8/13) Feb 28 2008 But in a fragile way, and bound to the language. A library solution that...
- Robert Fraser (15/77) Feb 28 2008 Why not just return the instance of MyClass? More flexible, since it
- Janice Caron (9/11) Feb 28 2008 No, that would be /less/ flexible, since the factory function would
- Robert Fraser (4/17) Feb 28 2008 Why not generate the string and then call Object.factory() from within
- Christopher Wright (13/26) Feb 28 2008 So, generate these strings at runtime? The solution:
- Janice Caron (3/10) Feb 28 2008 Again, that would require the factory function to have compile-time
- Jarrett Billingsley (5/15) Feb 29 2008 Not necessarily. If Derived were in a shared object, it would be loaded...
- Christopher Wright (7/19) Feb 29 2008 You could try reading the code you're commenting on :P though there was
- Bill Baxter (10/72) Feb 29 2008 I don't see why
Something Andrei said got me thinking. I have a need for so-called "virtual constructors", and of course D has Object.factory() which just does the job, given the classname. (/Much/ nicer than C++!). But, in my case, the discriminator string won't be the class name (which may be unknown to the user), but one of a number of IANA-registered strings, documented in some internet RFC somewhere. So to deal with that, I need a "factory". The usual way of doing this is, given string s = whatever; // official name something like: auto x = MyClassFactory.create(s); And that's OK, but it does mean that polymorphic creation is still "different" from normal creation. I got thinking, wouldn't it be nice if I could just do auto x = new MyClass(s); and have it work. Now bear in mind that MyClass is an /abstract/ class. It can never be constructed. Only subclasses can be constructed, and that, of course, is the job of a factory. What I'm suggesting, then, is just syntactic sugar. It won't give us anything we can't do already, but it will let us do it with a nicer syntax. To make it work, you'd have to allow a class to have a factory within itself, and so what I propose is the following syntax: abstract class MyClass { static string opFactory(/*params*/) { /* returns a string */ } } Notice that I typed the opFactory function to return a string. The idea is that auto x = new MyClass(s); would be rewritten by the compiler as auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s))); whenever the class is abstract and static opFactoryis present. So what opNew needs to return is the string to pass to Object.factory. Observe the explicit cast in the rewritten line. That is there partly so that x gets the right type (at compile time), but also to ensure at runtime that the factory-created class is in fact a subclass of MyClass! (enforce is one of Andrei's new templates that throws an exception if the value is non-zero. I think it will also check for null, but if not, I'm sure it can be made to). I'm sure that better alternative names for opFactory can be thought up. Off the top of my head, I came up with opNew, opClass, opNewName, opClassName and opFactoryName. Thoughts?
Feb 28 2008
"Janice Caron" wroteSomething Andrei said got me thinking. I have a need for so-called "virtual constructors", and of course D has Object.factory() which just does the job, given the classname. (/Much/ nicer than C++!). But, in my case, the discriminator string won't be the class name (which may be unknown to the user), but one of a number of IANA-registered strings, documented in some internet RFC somewhere. So to deal with that, I need a "factory". The usual way of doing this is, given string s = whatever; // official name something like: auto x = MyClassFactory.create(s); And that's OK, but it does mean that polymorphic creation is still "different" from normal creation. I got thinking, wouldn't it be nice if I could just do auto x = new MyClass(s); and have it work. Now bear in mind that MyClass is an /abstract/ class. It can never be constructed. Only subclasses can be constructed, and that, of course, is the job of a factory. What I'm suggesting, then, is just syntactic sugar. It won't give us anything we can't do already, but it will let us do it with a nicer syntax. To make it work, you'd have to allow a class to have a factory within itself, and so what I propose is the following syntax: abstract class MyClass { static string opFactory(/*params*/) { /* returns a string */ } } Notice that I typed the opFactory function to return a string. The idea is that auto x = new MyClass(s); would be rewritten by the compiler as auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s))); whenever the class is abstract and static opFactoryis present. So what opNew needs to return is the string to pass to Object.factory. Observe the explicit cast in the rewritten line. That is there partly so that x gets the right type (at compile time), but also to ensure at runtime that the factory-created class is in fact a subclass of MyClass! (enforce is one of Andrei's new templates that throws an exception if the value is non-zero. I think it will also check for null, but if not, I'm sure it can be made to). I'm sure that better alternative names for opFactory can be thought up. Off the top of my head, I came up with opNew, opClass, opNewName, opClassName and opFactoryName. Thoughts?what about static opCall? class MyClass { static MyClass opCall(string s) { // have to implement factoryString return cast(MyClass)Object.factory(factoryString(s)); } } auto x = MyClass(s); then you could also make the opCall method a mixin to avoid reimplementation. -Steve
Feb 28 2008
On 28/02/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:what about static opCall?Ooh - I never thought of that. That's close. In the past, what I've done is to build a standalone function (not a member function) called new_MyClass(), which is only an underscore different from the standard "new" function. Ditching the word "new" altogether is attractive. But to clarify - it's the syntactic sugar of being able to say new AbstractBaseClass(...) (and end up with a derived class) that I was asking for. I am well aware that we already have the functionality, but the point of the request /is/ the sugar. It's not that I want the syntax to be "simpler than it is now", or something, it's that I want it to be /identical/ other uses of new. It's just one of those nice little touches that would make D seem more cool. :-)
Feb 28 2008
Janice Caron wrote:On 28/02/2008, Steven Schveighoffer <schveiguy yahoo.com> wrote:how about allow this functions in abstract classes abstract class AbstractBaseClass { AbstractBaseClass this(string s, int i) { switch(s) { case "Foo": return new DerivedClass(i); ... default: return null; // check code causes exception } } } the return type could be used to detect the usage.what about static opCall?Ooh - I never thought of that. That's close. In the past, what I've done is to build a standalone function (not a member function) called new_MyClass(), which is only an underscore different from the standard "new" function. Ditching the word "new" altogether is attractive. But to clarify - it's the syntactic sugar of being able to say new AbstractBaseClass(...)(and end up with a derived class) that I was asking for. I am well aware that we already have the functionality, but the point of the request /is/ the sugar. It's not that I want the syntax to be "simpler than it is now", or something, it's that I want it to be /identical/ other uses of new. It's just one of those nice little touches that would make D seem more cool. :-)
Feb 28 2008
On 28/02/2008, BCS <BCS pathlink.com> wrote:AbstractBaseClass this(string s, int i) { switch(s) { case "Foo": return new DerivedClass(i); ... default: return null; // check code causes exception } }The switch is the one thing that you /must not have/. The factory cannot possibly know all possible (present and future) derived classes. Putting that switch statement there prevents anyone without access to the source code from making new derived classes, making the mechanism completely unworkable for a library. Also, this mechanism needs to import all of the module(s) where the derived class(es) is/are defined, which is also bad. Also, the function above would cause "executable bloat", whereby every possible derived class will end up in every executable which calls that function, whether needed or not. The good way to implement a factory function is to call the function object.Factory(). That way, the factory function needs /no knowledge/ of the derived class, beyond it's class name, extendability is retained, and executable bloat is avoided.
Feb 28 2008
Janice Caron wrote:On 28/02/2008, BCS <BCS pathlink.com> wrote:your point is valid (in some cases) but totally orthogonal to mine. Re wright it as this: AbstractBaseClass this(string s, int i) { // code to return a sub type of // AbstractBaseClass based on value of s // or null on an argument error so that // check code causes exception } and no important feature of my suggestion is changed but your point vanishes.AbstractBaseClass this(string s, int i) { switch(s) { case "Foo": return new DerivedClass(i); ... default: return null; } }The switch is the one thing that you /must not have/. The factory cannot possibly know all possible (present and future) derived classes.But it might /in some cases/ known all the /valid/ onesPutting that switch statement there prevents anyone without access to the source code from making new derived classes,Re-read that last, but in a "good thing" context. in some cases it will be a good thing.making the mechanism completely unworkable for a library.over generalized: unworkable for /some/ (or even many) librariesAlso, this mechanism needs to import all of the module(s) where the derived class(es) is/are defined, which is also bad.In line with the above point of view; this is unavoidable for some cases.Also, the function above would cause "executable bloat", whereby every possible derived class will end up in every executable which calls that function, whether needed or not.Every possible derived class that can get used at run time will have to be available some how for any solution. The only escape would be lazy loading of DLL's or SO's and that can be used in my solution as well.The good way to implement a factory function is to call the function object.Factory(). That way, the factory function needs[the function will have] /no knowledge/ of the derived class, beyond it's class name,read that in a "bad thing" context. Again, it can be. BTW, I was not proposing that my solution is a better way to do what you are looking to do, I'm proposing it as a clean solution that can do something more general than what you are looking for.
Feb 28 2008
Janice Caron wrote:On 28/02/2008, BCS <BCS pathlink.com> wrote:A better way: abstract class AbstractBaseClass { // that type is probably wrong; I can't comprehend how to name // delegate types static AbstractBaseClass delegate ()[string] builders; static AbstractBaseClass factory(string str) { return builders[str](); } } class Derived : AbstractBaseClass { static this () { AbstractBaseClass.builders["something"] = { return new Derived(); } } }AbstractBaseClass this(string s, int i) { switch(s) { case "Foo": return new DerivedClass(i); ... default: return null; // check code causes exception } }The switch is the one thing that you /must not have/. The factory cannot possibly know all possible (present and future) derived classes. Putting that switch statement there prevents anyone without access to the source code from making new derived classes, making the mechanism completely unworkable for a library. Also, this mechanism needs to import all of the module(s) where the derived class(es) is/are defined, which is also bad. Also, the function above would cause "executable bloat", whereby every possible derived class will end up in every executable which calls that function, whether needed or not. The good way to implement a factory function is to call the function object.Factory(). That way, the factory function needs /no knowledge/ of the derived class, beyond it's class name, extendability is retained, and executable bloat is avoided.
Feb 28 2008
Janice Caron wrote:Now bear in mind that MyClass is an /abstract/ class. It can never be constructed. Only subclasses can be constructed, and that, of course, is the job of a factory. What I'm suggesting, then, is just syntactic sugar. It won't give us anything we can't do already, but it will let us do it with a nicer syntax.But in a fragile way, and bound to the language. A library solution that takes the pain out of writing factories would be more general, more flexible, and less work for Walter. And, hey, it probably does 80% of what you need, today. Except for you having to map IANA strings to classes. And since major dependency injection libraries don't support this, you're SOL: it's not enough of a use case to include there, so it certainly isn't enough of a use case for a language solution.
Feb 28 2008
Janice Caron wrote:Something Andrei said got me thinking. I have a need for so-called "virtual constructors", and of course D has Object.factory() which just does the job, given the classname. (/Much/ nicer than C++!). But, in my case, the discriminator string won't be the class name (which may be unknown to the user), but one of a number of IANA-registered strings, documented in some internet RFC somewhere. So to deal with that, I need a "factory". The usual way of doing this is, given string s = whatever; // official name something like: auto x = MyClassFactory.create(s); And that's OK, but it does mean that polymorphic creation is still "different" from normal creation. I got thinking, wouldn't it be nice if I could just do auto x = new MyClass(s); and have it work. Now bear in mind that MyClass is an /abstract/ class. It can never be constructed. Only subclasses can be constructed, and that, of course, is the job of a factory. What I'm suggesting, then, is just syntactic sugar. It won't give us anything we can't do already, but it will let us do it with a nicer syntax. To make it work, you'd have to allow a class to have a factory within itself, and so what I propose is the following syntax: abstract class MyClass { static string opFactory(/*params*/) { /* returns a string */ } } Notice that I typed the opFactory function to return a string. The idea is that auto x = new MyClass(s); would be rewritten by the compiler as auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s))); whenever the class is abstract and static opFactoryis present. So what opNew needs to return is the string to pass to Object.factory. Observe the explicit cast in the rewritten line. That is there partly so that x gets the right type (at compile time), but also to ensure at runtime that the factory-created class is in fact a subclass of MyClass! (enforce is one of Andrei's new templates that throws an exception if the value is non-zero. I think it will also check for null, but if not, I'm sure it can be made to). I'm sure that better alternative names for opFactory can be thought up. Off the top of my head, I came up with opNew, opClass, opNewName, opClassName and opFactoryName. Thoughts?Why not just return the instance of MyClass? More flexible, since it doesn't need to have a default constructor in the general case: abstract class MyClass { static string opFactory(/*params*/) { /* returns a MyClass */ } } That would cover the more common case of all the factory's types being known by the implmentation. Then for the case you needed, you can explicitly call Object#factory(string) from within the static opFactory and explicitly do the cast. Still just as nice on the call site, but more flexible for the implementor.
Feb 28 2008
On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:Why not just return the instance of MyClass? More flexible, since it doesn't need to have a default constructor in the general case:No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
Feb 28 2008
Janice Caron wrote:On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:Why not generate the string and then call Object.factory() from within that virtual constructor if you need the functionality, and for a typical factory that knows about all the subclasses, allow the cool syntax?Why not just return the instance of MyClass? More flexible, since it doesn't need to have a default constructor in the general case:No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
Feb 28 2008
Janice Caron wrote:On 28/02/2008, Robert Fraser <fraserofthenight gmail.com> wrote:So, generate these strings at runtime? The solution: AbstractBaseClass delegate () [string] builders; class Derived : AbstractBaseClass { static this () { builders["whatever"] = { return new Derived1(); }; } } Of course, you're just talking about syntactic sugar. In this case, I'm quite opposed, since it violates the typical expectation that typeof (new T) == T.Why not just return the instance of MyClass? More flexible, since it doesn't need to have a default constructor in the general case:No, that would be /less/ flexible, since the factory function would have to know about every possible derived class at compile time. The whole point of a generic factory function is that it has no compile-time knowledge of the derived classes. /All/ information about derived classes is delayed until runtime. The only practical way to do that is to call Object.factory(), and since Object.factory() takes a string parameter, a string is what the function needs to supply.
Feb 28 2008
On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:class Derived : AbstractBaseClass { static this () { builders["whatever"] = { return new Derived1(); }; } }Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
Feb 28 2008
"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.89.1204269640.2351.digitalmars-d puremagic.com...On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:Not necessarily. If Derived were in a shared object, it would be loaded and then its static this() run, which would add itself to the list of builders in the base class, entirely at runtime.class Derived : AbstractBaseClass { static this () { builders["whatever"] = { return new Derived1(); }; } }Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
Feb 29 2008
Janice Caron wrote:On 29/02/2008, Christopher Wright <dhasenan gmail.com> wrote:You could try reading the code you're commenting on :P though there was a typo, I admit. It was the derived class adding a delegate to a static array in the base class. The base class doesn't, technically speaking, know anything about any derived classes, and you don't get any of that circular dependency madness.class Derived : AbstractBaseClass { static this () { builders["whatever"] = { return new Derived1(); }; } }Again, that would require the factory function to have compile-time knowledge of derived classes, which is precisely the thing to avoid.
Feb 29 2008
Janice Caron wrote:Something Andrei said got me thinking. I have a need for so-called "virtual constructors", and of course D has Object.factory() which just does the job, given the classname. (/Much/ nicer than C++!). But, in my case, the discriminator string won't be the class name (which may be unknown to the user), but one of a number of IANA-registered strings, documented in some internet RFC somewhere. So to deal with that, I need a "factory". The usual way of doing this is, given string s = whatever; // official name something like: auto x = MyClassFactory.create(s); And that's OK, but it does mean that polymorphic creation is still "different" from normal creation. I got thinking, wouldn't it be nice if I could just do auto x = new MyClass(s); and have it work. Now bear in mind that MyClass is an /abstract/ class. It can never be constructed. Only subclasses can be constructed, and that, of course, is the job of a factory. What I'm suggesting, then, is just syntactic sugar. It won't give us anything we can't do already, but it will let us do it with a nicer syntax. To make it work, you'd have to allow a class to have a factory within itself, and so what I propose is the following syntax: abstract class MyClass { static string opFactory(/*params*/) { /* returns a string */ } } Notice that I typed the opFactory function to return a string. The idea is that auto x = new MyClass(s); would be rewritten by the compiler as auto x = enforce(cast(MyClass)Object.factory(MyClass.opFactory(s))); whenever the class is abstract and static opFactoryis present. So what opNew needs to return is the string to pass to Object.factory. Observe the explicit cast in the rewritten line. That is there partly so that x gets the right type (at compile time), but also to ensure at runtime that the factory-created class is in fact a subclass of MyClass! (enforce is one of Andrei's new templates that throws an exception if the value is non-zero. I think it will also check for null, but if not, I'm sure it can be made to). I'm sure that better alternative names for opFactory can be thought up. Off the top of my head, I came up with opNew, opClass, opNewName, opClassName and opFactoryName. Thoughts?I don't see why auto x = new MyClass(s); is any better than auto x = MyClass.create(s); Except the first looks like normal construction, even though it's not, while the second screams out clearly "I'm a static factory method". So the second one seems preferable to me. Less magic going on to keep track of in your head. --bb
Feb 29 2008