www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Selftype: an idea from Scala for D2.0

reply eao197 <eao197 intervale.ru> writes:
	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
next sibling parent reply Henning Hasemann <hhasemann web.de> writes:
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
parent reply eao197 <eao197 intervale.ru> writes:
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
parent reply "David B. Held" <dheld codelogicconsulting.com> writes:
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
parent reply eao197 <eao197 intervale.ru> writes:
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
parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
eao197 wrote:

 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?

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.
May 26 2007
prev sibling next sibling parent reply Jari-Matti =?UTF-8?B?TcOka2Vsw6Q=?= <jmjmak utu.fi.invalid> writes:
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
next sibling parent reply eao197 <eao197 intervale.ru> writes:
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) */
    }
 }

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.

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.html
 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
parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
eao197 wrote:
 On Sat, 26 May 2007 00:30:11 +0400, Jari-Matti Mkel
 <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) */
    }
 }



 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 :)
May 26 2007
parent eao197 <eao197 intervale.ru> writes:
On Sat, 26 May 2007 14:28:27 +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?

Do you what [sic] to see Scala example?


Sorry, it is a typo. I mean: 'Do you want 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
prev sibling parent eao197 <eao197 intervale.ru> writes:
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
prev sibling parent reply Reiner Pope <some address.com> writes:
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.

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
parent reply eao197 <eao197 intervale.ru> writes:
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'=

 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
parent reply Reiner Pope <some address.com> writes:
eao197 wrote:
 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_ = 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.

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.

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); } -- Reiner
May 26 2007
parent reply eao197 <eao197 intervale.ru> writes:
On Sun, 27 May 2007 09:26:56 +0400, Reiner Pope <some address.com> wrote:

 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.

Sensor is Object. But may be situations in such a class must be inherited from some domain-specific base class.

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).

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 Akhotnikau
May 26 2007
parent reply Reiner Pope <some address.com> writes:
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.

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
parent eao197 <eao197 intervale.ru> writes:
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