View mode: basic / threaded / horizontal-split · Log in · Help
May 25, 2007
Selftype: an idea from Scala for D2.0
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) */
  }
}

Class SubjectObserver is used as special namespace for classes Subject and  
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 implementation.

The main points of interest are (2) and (4). Method 'subscribe' in Subject  
class receives parameter 'obs' as O, not as Observer. And method 'notify'  
in Observer class receive parameter 'sub' as S, not as Subject. But which  
type has 'this' at point (3) when Subject calls 'nofity' methods for each  
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). Because  
of that call of 'notify' in point (3) is legal.

Example of use that SubjectObserver:

object SensorReader extends SubjectObserver {
  type S = Sensor;  /* (5) */
  type O = Display; /* (6) */
  abstract class Sensor extends Subject {
    val label: String;
    var value: double = 0.0;
    def changeValue(v: double) = {
      value = v;
      publish;
    }
  }
  class Display extends Observer {
    def println(s: String) = ...
    def notify(sub: Sensor) =
      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 an  
implementation of Observer. Note that Display.notify receives Sensor, not  
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 = Account;
  type O = 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_ ~= 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 = cast(Sensor)sub; ... } /* (9) */
}
class Account : Subject { ... }
class Notificator : Observer { ... }

auto sensor = new Sensor;
auto display = new Display;
auto account = new Account;
auto mailNotificator = 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 templates  
as:

template SubjectObserver(O,S)
  {
    abstract class Subject
      {
        private O[] observers_ = [];
        public void subscribe( O observer )
          {
            observers_ ~= 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_ = label.dup;
        value_ = value;
      }

    Sensor self() { return this; } /* (12) */

    char[] label() { return label_; }
    double value() { return value_; }
    void value( double v ) { value_ = v; publish; }
  }

class Display : SensorReader.Observer
  {
    override void notify( Sensor subj )
      {
        Stdout( subj.label )( " has value " )( subj.value ).newline;
      }
  }

void main()
  {
    auto s1 = new Sensor( "First", 0 );
    auto s2 = new Sensor( "Second", 0 );
    auto d1 = new Display;
    auto d2 = new Display;

    s1.subscribe( d1 ); s1.subscribe( d2 );
    s2.subscribe( d1 );

    s1.value = 2;
    s2.value = 3;
  }

But here it is necessary to have method 'self' (point (12)) and use 'self'  
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 inside  
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
Re: Selftype: an idea from Scala for D2.0
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
Re: Selftype: an idea from Scala for D2.0
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 called  
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 = new MyStream;
auto my = new MyClass;
my.dump( myStream );  // Method MyStream.put(MyClass) must be called.

-- 
Regards,
Yauheni Akhotnikau
May 25, 2007
Re: Selftype: an idea from Scala for D2.0
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 26, 2007
Re: Selftype: an idea from Scala for D2.0
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 26, 2007
Re: Selftype: an idea from Scala for D2.0
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 26, 2007
Re: Selftype: an idea from Scala for D2.0
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 26, 2007
Re: Selftype: an idea from Scala for D2.0
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) */
>>>    }
>>> }

>> 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
Re: Selftype: an idea from Scala for D2.0
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
Re: Selftype: an idea from Scala for D2.0
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
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home