digitalmars.D - Selftype: an idea from Scala for D2.0
- eao197 (204/204) May 25 2007 Hi!
- Henning Hasemann (8/8) May 25 2007 I must confess I didnt read your whole posting to the end,
-
eao197
(73/77)
May 25 2007
On Fri, 25 May 2007 21:48:10 +0400, Henning Hasemann
... - David B. Held (36/88) May 25 2007 Your use case is a form of metatyping. In the Subject/Observer example,...
- eao197 (7/22) May 25 2007 Could you rewrite my initial example with Subject/Observer with using CR...
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (3/29) May 26 2007 You're using it already in the first post. The two classes are packaged
- Jari-Matti =?UTF-8?B?TcOka2Vsw6Q=?= (8/32) May 25 2007 I can see how this removes the need for "unsafe" casts. However, it migh...
- eao197 (10/32) May 25 2007 I'm not a Scala programmer so I don't know what the current situation wi...
- Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= (2/26) May 26 2007 Yes, please :)
- eao197 (10/17) May 26 2007 Sorry, it is a typo. I mean: 'Do you want to see Scala example?'
- eao197 (30/34) May 29 2007 More complex example is here:
- Reiner Pope (9/58) May 26 2007 I think mixins actually are the best way to do this. Here's how they
- eao197 (11/36) May 26 2007 =
- Reiner Pope (36/69) May 26 2007 I simply can't see the situation you describe. Suppose we had a
- eao197 (28/39) May 26 2007 That is not a desired situation :)
- Reiner Pope (14/43) May 27 2007 I see, you're right. :)
- eao197 (6/17) May 27 2007 Wow! Thank!
Hi! There is an interesting article Scalable Component Abstractions = (http://lamp.epfl.ch/~odersky/papers/ScalableComponent.pdf) which = describes some features of Scala language (http://www.scala-lang.org) fo= r = building scalable software components. Two of these features, abstract = members and selftypes, are illustrated by the following example of = Subject/Observer pattern implementation: abstract class SubjectObserver { type S <: Subject; type O <: Observer; abstract class Subject requires S { /* (1) */ private var observers: List[O] =3D List(); def subscribe(obs: O) =3D /* (2) */ observers =3D obs :: observers; def publish =3D for (val obs <observers) obs.notify(this); /* (3) */ } abstract class Observer { def notify(sub: S): unit; /* (4) */ } } Class SubjectObserver is used as special namespace for classes Subject a= nd = Observer. Type names S and O are declared as abstract members with = restructions: S must be subclass of Subject, and O must be subclass of = Observer. Concrete types for S and O must be defined by user of class = SubjectObserver. Class Subject is the base class for all future subject's implementations= . = Class Observer is the base class for all future observer's implementatio= n. The main points of interest are (2) and (4). Method 'subscribe' in Subje= ct = class receives parameter 'obs' as O, not as Observer. And method 'notify= ' = in Observer class receive parameter 'sub' as S, not as Subject. But whic= h = type has 'this' at point (3) when Subject calls 'nofity' methods for eac= h = observers? One could expect that 'this' has type Subject. But if it is = true, then calling 'notify' is illegal, since Observer.notify need S = (which should be subclass of Subject), not Subject. The trick is in point (1). The construct: class Subject requires S specifies that 'this' in Subject must have type S (or S subclass). Becau= se = of that call of 'notify' in point (3) is legal. Example of use that SubjectObserver: object SensorReader extends SubjectObserver { type S =3D Sensor; /* (5) */ type O =3D Display; /* (6) */ abstract class Sensor extends Subject { val label: String; var value: double =3D 0.0; def changeValue(v: double) =3D { value =3D v; publish; } } class Display extends Observer { def println(s: String) =3D ... def notify(sub: Sensor) =3D println(sub.label + " has value " + sub.value); } } The concrete types for defining S and O are specified at points (5) and = = (6). Here Sensor class is an implementation of Subject, and Display is a= n = implementation of Observer. Note that Display.notify receives Sensor, no= t = Subject. And Sensor.subscribe (inherited from SubjectObserver#Subject) = receives Display, not Observer. Because of that it is impossible to mix = = different subtypes of Subject/Observers: object AccountNotificator extends SubjectObserver { type S =3D Account; type O =3D Notificator; class Account extends Subject { ... } class Notificator extends Observer { ... } } import SensorReader._ import AccountNotificator._ val sensor: new Sensor val display: new Display val account: new Account val mailNotificator: new Notificator sensor.subscribe( display ) // legal account.subscribe( mailNotificator ) // legal account.subscribe( display ) // ILLEGAL!!! That error will be caught at compile time. This approach is different from naive object-oriented implementation of = = Subject/Observer: class Subject { private Observer[] observers_; void subscribe( Observer obs ) { observers_ ~=3D obj; } void publish() { foreach( obs; observers_ ) obs.notify( this ); } /* = (7) = */ } class Observer { abstract void notify( Subject ); /* (8) */ } class Sensor : Subject { ... } class Display : Observer { void notify( Subject sub ) { auto s =3D cast(Sensor)sub; ... } /* (9)= */ } class Account : Subject { ... } class Notificator : Observer { ... } auto sensor =3D new Sensor; auto display =3D new Display; auto account =3D new Account; auto mailNotificator =3D new Notificator; sensor.subscribe( display ); // Ok. account.subscribe( mailNotificator ); // Ok. account.subscribe( display ); // O-ops! That error will be caught only at run-time. I can implement Scala's solution in the current version of D via templat= es = as: template SubjectObserver(O,S) { abstract class Subject { private O[] observers_ =3D []; public void subscribe( O observer ) { observers_ ~=3D observer; } public void publish() { foreach( o; observers_ ) o.notify( self ); /* (10) */ } abstract S self(); /* (11) */ } abstract class Observer { abstract void notify( S subj ); } } alias SubjectObserver!(Display, Sensor) SensorReader; class Sensor : SensorReader.Subject { private char[] label_; private double value_; this( char[] label, double value ) { label_ =3D label.dup; value_ =3D value; } Sensor self() { return this; } /* (12) */ char[] label() { return label_; } double value() { return value_; } void value( double v ) { value_ =3D v; publish; } } class Display : SensorReader.Observer { override void notify( Sensor subj ) { Stdout( subj.label )( " has value " )( subj.value ).newline; } } void main() { auto s1 =3D new Sensor( "First", 0 ); auto s2 =3D new Sensor( "Second", 0 ); auto d1 =3D new Display; auto d2 =3D new Display; s1.subscribe( d1 ); s1.subscribe( d2 ); s2.subscribe( d1 ); s1.value =3D 2; s2.value =3D 3; } But here it is necessary to have method 'self' (point (12)) and use 'sel= f' = instead of 'this' (point (11)). It will be cool if D2.0 will allow something like: template SubjectObserver(O,S) { abstract class Subject is S { /* (13) */ ... void publish() { foreach( o; observers_ ) o.notify( this ); /* (14) */ } ... The construction 'class Subject is S' (point (13)) tells compiler that = 'this' will have type 'S' (point (14)) instead of Subject. That = construction (i.e. 'class A is B') should be applicable to classes insid= e = templates only. Disclaimer. Yes, I know that Sensor could mixin Subject, not inherit it.= = But I think there is probability that in some situation inheritance must= = be used by some very important reasons. -- = Regards, Yauheni Akhotnikau
May 25 2007
I must confess I didnt read your whole posting to the end, but it seems that this is much the same idea as I had in "Feature Request: interfaces declare types" plus some extra intelligence, or? Henning -- GPG Public Key: http://keyserver.veridis.com:11371/search?q=0x41911851 Fingerprint: 344F 4072 F038 BB9E B35D E6AB DDD6 D36D 4191 1851
May 25 2007
On Fri, 25 May 2007 21:48:10 +0400, Henning Hasemann <hhasemann web.de> = = wrote:I must confess I didnt read your whole posting to the end, but it seems that this is much the same idea as I had in "Feature Request: interfaces declare types" plus some extra intelligence, or?As I can see your idea is very close to 'abstract members' in Scala: class Demo { type A // This member must be defined in a derived class. // There isn't any restruction to type A. type B <: Another // This is another member which must be // defined in a derived class. /* ... In the body of class Demo names A and B can be used as template parameters in D templates ... */ } This looks similar to your proposal: interface ContainerIterator(T) { T content(); } interface Container(T) { type Iterator : ContainerIterator!(T); // Like Scala's 'type Iterator= <: = ContainerIterator[T]' T first(); void remove(T); Iterator(T) firstIterator(); void remove(Iterator(T)); } JFYI: There is a citation from 'Scalable Component Abstractions': \begin{quote} ... Abstract type members provide a fexible way to abstract over concrete = types of components. Abstract types can hide information about internals= = of a component, similar to their use in SML signatures. In an = object-oriented framework where classes can be extended by inheritance, = = they may also be used as a fexible means of parameterization (often call= ed = family polymorphism [11]). ... [11] E. Ernst. Family polymorphism. In Proceedings of the European = Conference on Object-Oriented Programming, pages 303-326, Budapest, = Hungary, 2001. \end{quote} So it seems that you reinvent the idea about 'abstract type members' ;) But I speak about another Scala's feature: selftypes. For example: template Demo(S) { class Dumpable { void dump(S stream) { stream.put( this ); // At this point 'this' is MyClass. } } } My proposal is to tell the compiler that 'this' in MyClass has type T or= = any of its subclass: template Demo(S, T) { class Dumpable is T { void dump(S stream) { stream.put( this ); // At this point 'this' is T. } } } class MyStream { void put( MyClass obj ) { ... } } class MyClass : Demo!( MyStream, MyClass ).Dumpable { ... } auto myStream =3D new MyStream; auto my =3D new MyClass; my.dump( myStream ); // Method MyStream.put(MyClass) must be called. -- = Regards, Yauheni Akhotnikau
May 25 2007
eao197 wrote:On Fri, 25 May 2007 21:48:10 +0400, Henning Hasemann <hhasemann web.de> wrote: [...] class Demo { type A // This member must be defined in a derived class. // There isn't any restruction to type A. type B <: Another // This is another member which must be // defined in a derived class. /* ... In the body of class Demo names A and B can be used as template parameters in D templates ... */ }Your use case is a form of metatyping. In the Subject/Observer example, you notice that the types S and O may be template parameters, but you want to restrict their type to be subtypes of the nested Subject/Observer types. That is, you want to provide metatypes for the S and O metaparameters.This looks similar to your proposal: interface ContainerIterator(T) { T content(); } interface Container(T) { type Iterator : ContainerIterator!(T); // Like Scala's 'type Iterator <: ContainerIterator[T]' T first(); void remove(T); Iterator(T) firstIterator(); void remove(Iterator(T)); }And this is also a form of metatyping. In this case, we want to declare that the dependent type Iterator(T) has a metatype of ContainerIterator(T) (that is, we minimize the set of possible values (types) that Iterator(T) can have by defining the nature of those values in terms of a metafunction...or, to put it another way, Iterator(T) is an algebraic metatype).JFYI: There is a citation from 'Scalable Component Abstractions': \begin{quote} [...] So it seems that you reinvent the idea about 'abstract type members' ;)Or rather, the CS community does not have a unified notion of metatyping and is inventing terms as they go.[...] My proposal is to tell the compiler that 'this' in MyClass has type T or any of its subclass: template Demo(S, T) { class Dumpable is T { void dump(S stream) { stream.put( this ); // At this point 'this' is T. } } } class MyStream { void put( MyClass obj ) { ... } } class MyClass : Demo!( MyStream, MyClass ).Dumpable { ... } [...]This particular example isn't very motivating because you would typically just do this: void dump(T)(S stream, T obj) { stream.put(obj); } In general, this is equivalent to the CRTP in C++, which has the general form in D of: class Derived : RecurringBase!(Derived) { ... } As with any recurrence pattern, you have to be careful about what is allowed and what isn't, or you may end up with an infinitely nested definition. For instance, if RecurringBase(T) is allowed to derive from T, you get the regress. However, D prevents this because T is a forward reference at instantiation time. I'm not sure what the value-add of the "self type" is when the CRTP is available in D: class Base(T) { T x; } class Derived : Base!(Derived) { int x; } void main() { Derived d; } This is a perfectly valid D program according to dmd. Dave
May 25 2007
On Sat, 26 May 2007 08:21:48 +0400, David B. Held <dheld codelogicconsulting.com> wrote:In general, this is equivalent to the CRTP in C++, which has the general form in D of: class Derived : RecurringBase!(Derived) { ... } As with any recurrence pattern, you have to be careful about what is allowed and what isn't, or you may end up with an infinitely nested definition. For instance, if RecurringBase(T) is allowed to derive from T, you get the regress. However, D prevents this because T is a forward reference at instantiation time. I'm not sure what the value-add of the "self type" is when the CRTP is available in D: class Base(T) { T x; } class Derived : Base!(Derived) { int x; } void main() { Derived d; } This is a perfectly valid D program according to dmd.Could you rewrite my initial example with Subject/Observer with using CRTP technique? -- Regards, Yauheni Akhotnikau
May 25 2007
eao197 wrote:On Sat, 26 May 2007 08:21:48 +0400, David B. Held <dheld codelogicconsulting.com> wrote:You're using it already in the first post. The two classes are packaged inside a single template so it might not seem so obvious.In general, this is equivalent to the CRTP in C++, which has the general form in D of: class Derived : RecurringBase!(Derived) { ... } As with any recurrence pattern, you have to be careful about what is allowed and what isn't, or you may end up with an infinitely nested definition. For instance, if RecurringBase(T) is allowed to derive from T, you get the regress. However, D prevents this because T is a forward reference at instantiation time. I'm not sure what the value-add of the "self type" is when the CRTP is available in D: class Base(T) { T x; } class Derived : Base!(Derived) { int x; } void main() { Derived d; } This is a perfectly valid D program according to dmd.Could you rewrite my initial example with Subject/Observer with using CRTP technique?
May 26 2007
eao197 wrote:Hi! There is an interesting article Scalable Component Abstractions (http://lamp.epfl.ch/~odersky/papers/ScalableComponent.pdf) which describes some features of Scala language (http://www.scala-lang.org) for building scalable software components. Two of these features, abstract members and selftypes, are illustrated by the following example of Subject/Observer pattern implementation: abstract class SubjectObserver { type S <: Subject; type O <: Observer; abstract class Subject requires S { /* (1) */ private var observers: List[O] List(); def subscribe(obs: O) /* (2) */ observers obs :: observers; def publish for (val obs <observers) obs.notify(this); /* (3) */ } abstract class Observer { def notify(sub: S): unit; /* (4) */ } }I can see how this removes the need for "unsafe" casts. However, it might cause some extra trouble to the planned reflection support :) From what I've read and heard, Scala does not yet support reflection. Can you show how this would work if there were multiple subclasses of 'Subject' and 'Observer'? (for example multiple sensor types in the SensorReader example) Does it require language support for multiple dispatch then?
May 25 2007
On Sat, 26 May 2007 00:30:11 +0400, Jari-Matti Mäkelä <jmjmak utu.fi.invalid> wrote:I'm not a Scala programmer so I don't know what the current situation with refelection is. But some support of refelection exits: http://www.scala-lang.org/docu/files/api/scala/reflect$content.htmlabstract class SubjectObserver { type S <: Subject; type O <: Observer; abstract class Subject requires S { /* (1) */ private var observers: List[O] List(); def subscribe(obs: O) /* (2) */ observers obs :: observers; def publish for (val obs <observers) obs.notify(this); /* (3) */ } abstract class Observer { def notify(sub: S): unit; /* (4) */ } }I can see how this removes the need for "unsafe" casts. However, it might cause some extra trouble to the planned reflection support :) From what I've read and heard, Scala does not yet support reflection.Can you show how this would work if there were multiple subclasses of 'Subject' and 'Observer'? (for example multiple sensor types in the SensorReader example) Does it require language support for multiple dispatch then?Do you what to see Scala example? -- Regards, Yauheni Akhotnikau
May 25 2007
eao197 wrote:On Sat, 26 May 2007 00:30:11 +0400, Jari-Matti Mäkelä <jmjmak utu.fi.invalid> wrote:abstract class SubjectObserver { type S <: Subject; type O <: Observer; abstract class Subject requires S { /* (1) */ private var observers: List[O] List(); def subscribe(obs: O) /* (2) */ observers obs :: observers; def publish for (val obs <observers) obs.notify(this); /* (3) */ } abstract class Observer { def notify(sub: S): unit; /* (4) */ } }Yes, please :)Can you show how this would work if there were multiple subclasses of 'Subject' and 'Observer'? (for example multiple sensor types in the SensorReader example) Does it require language support for multiple dispatch then?Do you what [sic] to see Scala example?
May 26 2007
On Sat, 26 May 2007 14:28:27 +0400, Jari-Matti Mäkelä <jmjmak utu.fi.invalid> wrote:Sorry, it is a typo. I mean: 'Do you want to see Scala example?'Can you show how this would work if there were multiple subclasses of 'Subject' and 'Observer'? (for example multiple sensor types in the SensorReader example) Does it require language support for multiple dispatch then?Do you what [sic] to see Scala example?Yes, please :)It is not a simple task for me, because I'm not a Scala programmer. I've tried to make a sample today but without success. So I ask my friends to help me with it. Now I'm waiting their replies. So I hope to publish such example some time later. -- Regards, Yauheni Akhotnikau
May 26 2007
On Sat, 26 May 2007 00:30:11 +0400, Jari-Matti Mäkelä <jmjmak utu.fi.invalid> wrote:Can you show how this would work if there were multiple subclasses of 'Subject' and 'Observer'? (for example multiple sensor types in the SensorReader example) Does it require language support for multiple dispatch then?More complex example is here: http://eao197.narod.ru/scala/AbstractTypesDemo.scala.html (thanks to aka50!) I've add yet another descendant of Sensor -- CalibrateableSensor with can use Calibrator for adjusting his values. Display can show values from Sensor and from CalibrateableSensor, but Calibrator can work only with CalibrateableSensor. Here is some difficalty in extending classes with selftypes by inheritance in Scala. It is impossible to simple inherit CalbrateableSensor from Sensor and add data necessary for calibration support to it. That is because it is necessary to define additional trait CalibrateableEntity and use it in 'requires' clause for CalibrateableSensor trait. In D construct 'class A is B' for classes in template may be more useful and less restricted: template SubjectObserver(S,O) { class Subject is S { ... } class Observer { ... } } template AbstractSensorReader(S,D) { class Sensor is S : SubjectObserver!(Sensor, D).Subject { ... } class Display : SubjectObserver!(S, D).Observer { ... } class CalibrateableSensor : Sensor { ... } class Calibrator { ... } } without need of additional interfaces an so on. -- Regards, Yauheni Akhotnikau
May 29 2007
eao197 wrote:Disclaimer. Yes, I know that Sensor could mixin Subject, not inherit it. But I think there is probability that in some situation inheritance must be used by some very important reasons.I think mixins actually are the best way to do this. Here's how they would look with your example (note that there is very little modification of the code):template Subject(O, S) { private O[] observers_ = []; public void subscribe(O observer) { observers_ ~= observer; } public void publish() { foreach ( o; observers_ ) o.notify( this ); } } interface Observer(S) { void notify(S subj); } class Sensor { mixin Subject!(Display, Sensor); private char[] label_; private double value_; this( char[] label, double value ) { label_ = label.dup; value_ = value; } char[] label() { return label_; } double value() { return value_; } void value( double v ) { value_ = v; publish; } } class Display : Observer!(Sensor) { void notify( Sensor subj ) { Stdout( subj.label )( " has value " )( subj.value ).newline; } }Using the syntax Subject is S automatically requires that any implementors of Subject!(Display, Sensor) inherit from Sensor. So why not simply make Sensor the inheritance root, as the mixin does? I don't see that any information is lost this way. -- Reiner
May 26 2007
On Sun, 27 May 2007 05:23:55 +0400, Reiner Pope <some address.com> wrote= :class Sensor { mixin Subject!(Display, Sensor); private char[] label_; private double value_; this( char[] label, double value ) { label_ =3D label.dup; value_ =3D value; } char[] label() { return label_; } double value() { return value_; } void value( double v ) { value_ =3D v; publish; } } class Display : Observer!(Sensor) { void notify( Sensor subj ) { Stdout( subj.label )( " has value " )( subj.value ).newline; } }Using the syntax Subject is S automatically requires that any =implementors of Subject!(Display, Sensor) inherit from Sensor. So why ==not simply make Sensor the inheritance root, as the mixin does? I don'=t =see that any information is lost this way.In that example Sensor doesn't inherit from Subject. The base type for = Sensor is Object. But may be situations in such a class must be inherited from some = domain-specific base class. -- = Regards, Yauheni Akhotnikau
May 26 2007
eao197 wrote:On Sun, 27 May 2007 05:23:55 +0400, Reiner Pope <some address.com> wrote:I simply can't see the situation you describe. Suppose we had a situation where you needed multiple classes to inherit from Subject!(Display, Sensor). Well, why would that be? Presumably so you can use them polymorhpically, perhaps something like the following: void main() { // Let's suppose we could actually have a base type // Subject!(Display, Sensor) from which everything // was derived. Suppose 'self' is possible. alias Subject!(Display, Sensor) SubjectType; SubjectType[] allSubjects = getListOfAllSubjects(); Observer!(Sensor) myGenericObserver = new Display(); foreach (s; allSubjects) s.subscribe(myGenericObserver); } For example, perhaps you have lots of things which need specific observers, and you additionally want to log everything using myGenericObserver. In that case, it is beneficial for all the classes to be inherited from Subject!(Display, Sensor). But the condition 'Subject is S' already requires that 'this' for any descendant of Subject!(Display, Sensor) can be implicitly converted to Sensor. Which means that any class which inherits from Subject!(Display, Sensor) also inherits from Sensor. So you can rewrite my example above as void main() { // Now we don't need to have a base // type Subject!(Display, Sensor). Instead, // our base type is simply Sensor (which is // implemented with a mixin) Sensor[] allSubjects = getListOfAllSubjects(); Observer!(Sensor) myGenericObserver = new Display(); foreach (s; allSubjects) s.subscribe(myGenericObserver); } -- ReinerIn that example Sensor doesn't inherit from Subject. The base type for Sensor is Object. But may be situations in such a class must be inherited from some domain-specific base class.class Sensor { mixin Subject!(Display, Sensor); private char[] label_; private double value_; this( char[] label, double value ) { label_ = label.dup; value_ = value; } char[] label() { return label_; } double value() { return value_; } void value( double v ) { value_ = v; publish; } } class Display : Observer!(Sensor) { void notify( Sensor subj ) { Stdout( subj.label )( " has value " )( subj.value ).newline; } }Using the syntax Subject is S automatically requires that any implementors of Subject!(Display, Sensor) inherit from Sensor. So why not simply make Sensor the inheritance root, as the mixin does? I don't see that any information is lost this way.
May 26 2007
On Sun, 27 May 2007 09:26:56 +0400, Reiner Pope <some address.com> wrote:That is not a desired situation :) I don't want multiple classes inherited from Subject!(Display, Sensor). I want to have multiple distinct classes inherited from Subject (for example: SubjectObserver!(Notificator, Account) for banking accounting system, SubjectObserver!(Display, Sensor) for data acquisition system, SubjectObserver!(Mailbox, Topic) for message delivery system and so on)). Class Subject in template SubjectObjserver may be inherited from another useful class (Serializabe, Loggable, Dumpable or something else). This means that in the following case: class Account : SubjectObserver!(Notificator, Account).Subject { ... } class Topic : SubjectObserver!(Mailbox, Topic).Subject { ... } classes Account and Topic will be automatically derived from Serializable (Loggable, Dumpable, ...) too. In case of: class Account { mixin Subject!(Notificator, Account); ... } class Topic { mixin Subject!(Mailbox, Topic); ... } it is neccessary to derive Account and Topic from Serializable (Loggable, Dumpable, ...) manually. -- Regards, Yauheni AkhotnikauI simply can't see the situation you describe. Suppose we had a situation where you needed multiple classes to inherit from Subject!(Display, Sensor).Using the syntax Subject is S automatically requires that any implementors of Subject!(Display, Sensor) inherit from Sensor. So why not simply make Sensor the inheritance root, as the mixin does? I don't see that any information is lost this way.In that example Sensor doesn't inherit from Subject. The base type for Sensor is Object. But may be situations in such a class must be inherited from some domain-specific base class.
May 26 2007
eao197 wrote:That is not a desired situation :) I don't want multiple classes inherited from Subject!(Display, Sensor). I want to have multiple distinct classes inherited from Subject (for example: SubjectObserver!(Notificator, Account) for banking accounting system, SubjectObserver!(Display, Sensor) for data acquisition system, SubjectObserver!(Mailbox, Topic) for message delivery system and so on)). Class Subject in template SubjectObjserver may be inherited from another useful class (Serializabe, Loggable, Dumpable or something else). This means that in the following case: class Account : SubjectObserver!(Notificator, Account).Subject { ... } class Topic : SubjectObserver!(Mailbox, Topic).Subject { ... } classes Account and Topic will be automatically derived from Serializable (Loggable, Dumpable, ...) too. In case of: class Account { mixin Subject!(Notificator, Account); ... } class Topic { mixin Subject!(Mailbox, Topic); ... } it is neccessary to derive Account and Topic from Serializable (Loggable, Dumpable, ...) manually.I see, you're right. :) As a side not, there's one pattern which could be interesting in this kind of situation: template Subject(O, S) { ... // Implementation details alias Tuple!(Loggable, Dumpable, ...) ImplementedInterfaces; } ... class Account : Subject!(Notifier, Account).ImplementedInterfaces { mixin Subject!(Notifier, Account); } -- Reiner
May 27 2007
On Sun, 27 May 2007 11:24:40 +0400, Reiner Pope <some address.com> wrote:As a side not, there's one pattern which could be interesting in this kind of situation: template Subject(O, S) { ... // Implementation details alias Tuple!(Loggable, Dumpable, ...) ImplementedInterfaces; } ... class Account : Subject!(Notifier, Account).ImplementedInterfaces { mixin Subject!(Notifier, Account); }Wow! Thank! It's very interesting idea. -- Regards, Yauheni Akhotnikau
May 27 2007