digitalmars.D - structs, classes, interfaces - Part III, Solution
- Martin Hinsch (78/78) Sep 01 2007 The basic idea behind my solution is actually really simple. You just ha...
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (19/45) Sep 01 2007 I'm not sure if I understood you correctly, but isn't this exactly the t...
- Daniel Keep (9/21) Sep 01 2007 I believe this will be possible using:
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (8/40) Sep 01 2007 I don't doubt it isn't possible, but it needs to be implemented first to...
- Reiner Pope (11/41) Sep 01 2007 I wonder if we can extend the alias this syntax to get rid of the
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (8/19) Sep 02 2007 Maybe I'm missing something, but why should alias be used in the first
- Reiner Pope (11/31) Sep 04 2007 I think it makes sense in the first place because one of alias's
- Martin Hinsch (26/80) Sep 01 2007 Yes, as I said, the "enhanced composition" part has been discussed
- Sean Kelly (4/8) Sep 01 2007 To sidestep the issue for a moment, do templates solve your problem for
- Martin Hinsch (15/26) Sep 01 2007 I feel a bit dumb but I have no idea what you mean... The basic problem ...
- Sean Kelly (4/18) Sep 01 2007 See my other post "implicit template function overloading broken?" for
- BCS (14/25) Sep 01 2007 An interesting idea. However I agree that it's not likely to make it in....
- Janice Caron (28/28) Sep 05 2007 -----Original Message-----
- Ben Hinkle (2/10) Sep 05 2007 This is the same as what I've been calling "typed pointers" in Cx: http:...
- Ben Hinkle (2/10) Sep 05 2007 This is the same as what I've been calling "typed pointers" in Cx: http:...
The basic idea behind my solution is actually really simple. You just have to realize that (OOP-) inheritance as it is usually done is a combination of two different concepts - composition and polymorphism. What would happen if we disentangled these concepts? My suggestion would be to completely remove virtual functions from the language. Instead use interfaces to provide ad hoc runtime polymorphism. As before interfaces define a set of methods a class has to provide. As before you can let a class derive from an interface to declare (and let the compiler check) its conformance to the interface. But now this is not obligatory anymore. There are now two fundamentally different types of pointers in the language: a) *non-polymorphic* pointers to plain data/classes b) *polymorphic* pointers to interfaces At any point a pointer of type a) can be cast to a pointer of type b) *at which point the conformance to the interface is tested*. This is easily done at compile time since in the non-polymorphic world all types are known. I'll give an example: --- class A { void foo(int a); } class B { void foo(int b); } interface Foo { void foo(int c); } A a; B b; Foo * f1 = &a, f2 = &b; f1.foo(42); // calls a.foo --- You could even introduce a way to construct an interface from a given class so that existing class hierarchies could remain completely unchanged: --- interface(B) * i = &b; --- Internally a pointer to an interface would actually be a struct containing the vtable and a pointer to the object which would be a tiny bit more costly than a class reference. Since polymorphism has now its own separate language structure composition can be made much more powerful (similar ideas have recently been discussed for structs): --- class A {...} class B {...} class C : A,B {...} // syntactic sugar for class C { A super_0; B super_1; use super_0; use super_1; } --- I think this solves all problems I mentioned above except one, which is slicing. To be honest I never saw the particular danger in that one in the first place. Maybe I'm totally missing something but isn't that just another case of a lossy cast? In that case my proposal solves that problem as well since all potentially lossy (slicy ;-) casts can be detected at compile time and the user warned. I have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it. -- Martin Hinsch m.hinsch rug.nl +31 50 363 8097 CEES Centre for Ecological and Evolutionary Studies Biologisch Centrum Kerklaan 30 Postbus 14 9750 AA Haren
Sep 01 2007
Martin Hinsch wrote:The basic idea behind my solution is actually really simple. You just have to realize that (OOP-) inheritance as it is usually done is a combination of two different concepts - composition and polymorphism. What would happen if we disentangled these concepts? My suggestion would be to completely remove virtual functions from the language. Instead use interfaces to provide ad hoc runtime polymorphism. As before interfaces define a set of methods a class has to provide. As before you can let a class derive from an interface to declare (and let the compiler check) its conformance to the interface. But now this is not obligatory anymore.I think this solves all problems I mentioned above except one, which is slicing. To be honest I never saw the particular danger in that one in the first place. Maybe I'm totally missing something but isn't that just another case of a lossy cast? In that case my proposal solves that problem as well since all potentially lossy (slicy ;-) casts can be detected at compile time and the user warned. I have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it.I'm not sure if I understood you correctly, but isn't this exactly the thing that was discussed recently. From language consistency POVclass A {...} class B {...} class C : A,B {...}is a bit problematic because D doesn't have multiple inheritance. A trait class construct could be possible though, but I cannot say if the added complexity to the core language is good. However, the interface compliance could be enforced that way (and Walter seems to like it too, see slides). Implementation could be mixed in with an extended mixin syntax: interface IA { void foo(); } interface IB { int bar(); } struct A : IA { void foo() { writefln("hello"); } struct B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A; mixin B; } I've noticed mixin is a powerful mechanism. MI is more or less a combination of interface part and mixed in implementation and provides a lot less flexibility.
Sep 01 2007
Jari-Matti Mäkelä wrote:... interface IA { void foo(); } interface IB { int bar(); } struct A : IA { void foo() { writefln("hello"); } struct B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A; mixin B; }I believe this will be possible using: struct C : IA, IB { private A a; private B b; alias a this; alias b this; } -- Daniel
Sep 01 2007
Daniel Keep wrote:Jari-Matti Mäkelä wrote:I don't doubt it isn't possible, but it needs to be implemented first too :) One problem I see in the new aliasing syntax is that it introduces two unnecessary symbols C.a and C.b as an side effect. (but it could allow you to choose between a.foo and b.foo if foo happens to conflict, though) Another way to implement this could be to allow "template inheritance". In that case only non-virtual inheritance model needs to be implemented, no new mixins or aliases:... interface IA { void foo(); } interface IB { int bar(); } struct A : IA { void foo() { writefln("hello"); } struct B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A; mixin B; }I believe this will be possible using: struct C : IA, IB { private A a; private B b; alias a this; alias b this; }template A : IA { void foo() { writefln("hello"); } template B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A!(); // or maybe even mixin A; when there are no arguments mixin B!(); }
Sep 01 2007
Jari-Matti Mäkelä wrote:Daniel Keep wrote:I wonder if we can extend the alias this syntax to get rid of the unnecessary symbols. Since, conceptually, aliasing just "binds" the second name to the first, giving the first variable a new name, why don't we just allow variables to be named "this". The code above becomes struct C : IA, IB { A this; B this; } To me it's quite consistent with "alias b this". -- ReinerJari-Matti Mäkelä wrote:I don't doubt it isn't possible, but it needs to be implemented first too :) One problem I see in the new aliasing syntax is that it introduces two unnecessary symbols C.a and C.b as an side effect. (but it could allow you to choose between a.foo and b.foo if foo happens to conflict, though)... interface IA { void foo(); } interface IB { int bar(); } struct A : IA { void foo() { writefln("hello"); } struct B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A; mixin B; }I believe this will be possible using: struct C : IA, IB { private A a; private B b; alias a this; alias b this; }
Sep 01 2007
Reiner Pope wrote:I wonder if we can extend the alias this syntax to get rid of the unnecessary symbols. Since, conceptually, aliasing just "binds" the second name to the first, giving the first variable a new name, why don't we just allow variables to be named "this". The code above becomes struct C : IA, IB { A this; B this; } To me it's quite consistent with "alias b this".Maybe I'm missing something, but why should alias be used in the first place? Currently it's only a weak typedef (extended to other symbols) with some overloading semantics. Adding mixin-like behavior inside structs feels a bit inconsistent to me at least. Would it also allow aliasing some symbols outside the struct? Doesn't it complicate metaprogramming a bit by mixing runtime and compile time code?
Sep 02 2007
Jari-Matti Mäkelä wrote:Reiner Pope wrote:I think it makes sense in the first place because one of alias's meanings is "give the first symbol another name." In the case of "alias B this", you are giving B the name, "this". Since all member accesses and member functions are of the form this.foo or this.func() (but the "this." may be omitted) it makes sense that if B gets the name "this", then it behaves like a member (as far as calling goes). -- ReinerI wonder if we can extend the alias this syntax to get rid of the unnecessary symbols. Since, conceptually, aliasing just "binds" the second name to the first, giving the first variable a new name, why don't we just allow variables to be named "this". The code above becomes struct C : IA, IB { A this; B this; } To me it's quite consistent with "alias b this".Maybe I'm missing something, but why should alias be used in the first place? Currently it's only a weak typedef (extended to other symbols) with some overloading semantics. Adding mixin-like behavior inside structs feels a bit inconsistent to me at least. Would it also allow aliasing some symbols outside the struct? Doesn't it complicate metaprogramming a bit by mixing runtime and compile time code?
Sep 04 2007
On Sat, 01 Sep 2007 16:20:44 +0300, Jari-Matti Mäkelä wrote:Martin Hinsch wrote:Yes, as I said, the "enhanced composition" part has been discussed recently. However only for structs and only in a very specific way. Anyways the point of my proposal is not that but rather that run-time polymorphism is completely separated from (and thus made orthogonal to) constructing classes from other classes (aka derivation). In the *second step* this takes a big burden from the class hierarchy and makes it possible to be much more expressive with glueing various bits and pieces together to create a new class. The great thing is that as soon as derivation *only* does composition you can treat it as a special case of a general class of patterns which involve taking pieces of data + functions and stick them together like lego blocks to build something new. This kind of pattern is actually quite common nowadays in C++, I believe. You take a bunch of light-weight classes and combine them as data representation, algorithm, etc. parts of a bigger class. You add run-time polymorphism by adding in an ABC which declares the required methods. My idea would make this last step much more elegant and simple. -- Martin Hinsch m.hinsch rug.nl +31 50 363 8097 CEES Centre for Ecological and Evolutionary Studies Biologisch Centrum Kerklaan 30 Postbus 14 9750 AA HarenThe basic idea behind my solution is actually really simple. You just have to realize that (OOP-) inheritance as it is usually done is a combination of two different concepts - composition and polymorphism. What would happen if we disentangled these concepts? My suggestion would be to completely remove virtual functions from the language. Instead use interfaces to provide ad hoc runtime polymorphism. As before interfaces define a set of methods a class has to provide. As before you can let a class derive from an interface to declare (and let the compiler check) its conformance to the interface. But now this is not obligatory anymore.I think this solves all problems I mentioned above except one, which is slicing. To be honest I never saw the particular danger in that one in the first place. Maybe I'm totally missing something but isn't that just another case of a lossy cast? In that case my proposal solves that problem as well since all potentially lossy (slicy ;-) casts can be detected at compile time and the user warned. I have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it.I'm not sure if I understood you correctly, but isn't this exactly the thing that was discussed recently. From language consistency POVclass A {...} class B {...} class C : A,B {...}is a bit problematic because D doesn't have multiple inheritance. A trait class construct could be possible though, but I cannot say if the added complexity to the core language is good. However, the interface compliance could be enforced that way (and Walter seems to like it too, see slides). Implementation could be mixed in with an extended mixin syntax: interface IA { void foo(); } interface IB { int bar(); } struct A : IA { void foo() { writefln("hello"); } struct B : IB { int bar() { return 42; } } struct C : IA, IB { mixin A; mixin B; } } I've noticed mixin is a powerful mechanism. MI is more or less a combination of interface part and mixed in implementation and provides a lot less flexibility.
Sep 01 2007
Martin Hinsch wrote:I have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it.To sidestep the issue for a moment, do templates solve your problem for arrays of structs, or is there some reason you need run-time polymorphism? Sean
Sep 01 2007
On Sat, 01 Sep 2007 09:34:31 -0700, Sean Kelly wrote:Martin Hinsch wrote:I feel a bit dumb but I have no idea what you mean... The basic problem is that I want to have a proper OOP type but with all the speed and memory advantages value semantics offer PLUS the option of painlessly "upgrading" it later to something even more OOPishy (i.e. rt-polymorphism, reference semantics). -- Martin Hinsch m.hinsch rug.nl +31 50 363 8097 CEES Centre for Ecological and Evolutionary Studies Biologisch Centrum Kerklaan 30 Postbus 14 9750 AA HarenI have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it.To sidestep the issue for a moment, do templates solve your problem for arrays of structs, or is there some reason you need run-time polymorphism? Sean
Sep 01 2007
Martin Hinsch wrote:On Sat, 01 Sep 2007 09:34:31 -0700, Sean Kelly wrote:See my other post "implicit template function overloading broken?" for an example. However, templates limit polymorphism to compile-time. SeanMartin Hinsch wrote:I feel a bit dumb but I have no idea what you mean... The basic problem is that I want to have a proper OOP type but with all the speed and memory advantages value semantics offer PLUS the option of painlessly "upgrading" it later to something even more OOPishy (i.e. rt-polymorphism, reference semantics).I have no illusions concerning the probability of this making it into D. Still I think it's a nice idea ;-) and I would be really interested in hearing what people think about it.To sidestep the issue for a moment, do templates solve your problem for arrays of structs, or is there some reason you need run-time polymorphism?
Sep 01 2007
Reply to Martin,The basic idea behind my solution is actually really simple. You just have to realize that (OOP-) inheritance as it is usually done is a combination of two different concepts - composition and polymorphism. What would happen if we disentangled these concepts? My suggestion would be to completely remove virtual functions from the language. Instead use interfaces to provide ad hoc runtime polymorphism.An interesting idea. However I agree that it's not likely to make it in. However another option that can be taken from the "oop is two things" standpoint is to allow composition in structs (the forthcoming "alias foo this;" gets much of that) You can already do this (I think) in a some what hackish manner in that you can treat a pointer to a struct as a pointer to the type of it's first member. A cleaner way to do things would be to allow a struct to inherent from another struct but with the restriction that the new struct can't override anything from the base struct and that casting to the derived type is an unsafe operation.Internally a pointer to an interface would actually be a struct containing the vtable and a pointer to the object which would be a tiny bit more costly than a class reference.I've wanted this for a LONG time. It would allow all sorts of fun things to implement an interface (functions, structs, maybe even basic types). Alas it would break COM or require a totally incompatible special case. (I darn to heck whoever defined the COM standard that way!)
Sep 01 2007
-----Original Message----- From: digitalmars-d-bounces puremagic.com [mailto:digitalmars-d-bounces puremagic.com] On Behalf Of Reiner Pope Sent: 04 September 2007 23:34 To: digitalmars-d puremagic.com Subject: Re: structs, classes, interfaces - Part III, Solution I think it makes sense in the first place because one of alias's meanings is "give the first symbol another name." In the case of "alias B this", you are giving B the name, "this". Since all member accesses and member functions are of the form this.foo or this.func() (but the "this." may be omitted) it makes sense that if B gets the name "this", then it behaves like a member (as far as calling goes). I definitely prefer the syntax struct B : A { } instead of struct B { A a; alias a this; // or any other variant such as A this } I would also like the following to work: B b; auto a = cast(A)(b); // syntactic sugar for auto a = b.a;
Sep 05 2007
This is the same as what I've been calling "typed pointers" in Cx: http://tinycx.sourceforge.net/typeptr.html. You can add methods to builtin types like "double" or "int". Cx is temporarily on the back burner so there are no downloads available but I hope to return to it soon. -BenInternally a pointer to an interface would actually be a struct containing the vtable and a pointer to the object which would be a tiny bit more costly than a class reference.I've wanted this for a LONG time. It would allow all sorts of fun things to implement an interface (functions, structs, maybe even basic types). Alas it would break COM or require a totally incompatible special case. (I darn to heck whoever defined the COM standard that way!)
Sep 05 2007
This is the same as what I've been calling "typed pointers" in Cx: http://tinycx.sourceforge.net/typeptr.html. You can add methods to builtin types like "double" or "int". Cx is temporarily on the back burner so there are no downloads available but I hope to return to it soon. -BenInternally a pointer to an interface would actually be a struct containing the vtable and a pointer to the object which would be a tiny bit more costly than a class reference.I've wanted this for a LONG time. It would allow all sorts of fun things to implement an interface (functions, structs, maybe even basic types). Alas it would break COM or require a totally incompatible special case. (I darn to heck whoever defined the COM standard that way!)
Sep 05 2007