Delegation 
Author Message
 Delegation

In some other OO languages, notably Smalltalk and Objective-C, it is a
common idiom to delegate[*] messages that are sent to one object to
another object. The interesting thing is that this is not done with
individual forwarding methods, but rather with one catch-all method that
relays all messages that can't be handled by the original receiver to a
delegate object.

Clearly, this can't be directly translated to a language such as Dylan
that doesn't follow the message passing paradigm. Nevertheless, I take
it that this is a useful idiom. Any ideas how to apply it in Dylan?

Michael

[*] A friend tells me that technically this is "consultation". In his
view delegation means that this/self/whatever still refers to the
original receiver.

--
Michael Schuerig

http://www.*-*-*.com/



Sat, 17 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

>In some other OO languages, notably Smalltalk and Objective-C, it is a
>common idiom to delegate[*] messages that are sent to one object to
>another object. The interesting thing is that this is not done with
>individual forwarding methods, but rather with one catch-all method that
>relays all messages that can't be handled by the original receiver to a
>delegate object.

   Isn't this about using the doesNotUnderstand mechanism...  if so
   this got covered back in August...
                                    ... but taking another stab at it.

   It would seem that you can you do this with generic functions also....

   define generic some-action  (  arg1 :: <object> , arg2 :: <object> ) ;

   define method  some-action  (  arg1 :: <object> , arg2 :: <object> )
   begin
        ... find the apropriate delagate
            .... and shuffle object and args in that direction as
                  apropriate....
   end method some-action ;

   The focus of where you insert your delgation code should be directed at
   the generic functions not with the classes (as in the case of Smalltalk
   and Obj. C, if I recall correctly). Generic functions "select" methods
   not classes.

   So for each generic function you wish to use without regard for
   there being an applicable method... you simply make an method that
   is applicable to all.  [ You'll have to be careful about using
   the next-method mechanism. It should rise to the level of this
   "least applicable method".  And I'm going leave the complications
    optional and keyed argments bring with them in the following. ]

   Now the ratio of methods to classes might be high. You could create a
   generic along the lines of  doesNotUnderstand:
   [ Presuming the first arg's class carries some significance as to how to
     resolve the delegation. ]

   define generic  doesNotUnderstand ( class-obj :: <class> ,
                                       gf-obj    :: <function> ,
                                       args      :: <sequence> )

   define method  doesNotUnderstand   (  class-obj == <foo-class> ,
                                          gf-obj   == some-action ,
                                          args  :: <sequence> )
   begin
     ...  a delegate just for the generic some-action when the first
         arg is a  direct instance of <foo-class> ...

   end method

   define method  doesNotUnderstand   (  class-obj :: <class>
                                          gf-obj   == some-action ,
                                          args  :: <sequence> )
   begin
     ...  a delegate just for the generic some-action when the first
         arg is a  any class.

   end method

   define method  doesNotUnderstand  (   class-obj == <foo-class> ,
                                         gf-obj :: <function> ,
                                         args :: <sequence> )
   begin
      ... find delegate through some table of generics that direct instances
           of <foo-class> specifically must use....
   end

   define method  doesNotUnderstand  (   class-obj <class> ,
                                         gf-obj :: <function> ,
                                         args :: <sequence> )
   begin
      ... find delegate through some data structure(s) keyed on generic
        functions and classes....
        (if you have enough methods based on singleton arguments
            you really don't need an explicit table. )
   end

 Essentially, "somebody" sets up the delegate relationship by making
 additions to the doesNotUnderstand generic function. The descrimination
 based upon instance class, generic function , or both.

 At this point the "least applicable" some-action need only call
 doesNotUnderstand and cobble up the args into a sequence. Example

   define method some-action ( arg1 :: <object>  , arg2 :: <object> )
   begin
        doesNotUnderstand ( object-class (arg1) ,
                            some-action,
                            vector ( arg1 , arg2 ) );
   end method some-action

 This would probably be better if declaring the generic and the
 "least applicable method"( LAM ) were encapsulated into a single
 macro. That way one couldn't "forget to do both".

  define generic-and-LAM some-action ( ... ) .... end generic-and-LAM

Quote:
>Clearly, this can't be directly translated to a language such as Dylan
>that doesn't follow the message passing paradigm. Nevertheless, I take
>it that this is a useful idiom. Any ideas how to apply it in Dylan?

 As stated above... if you want to "patch" the dispatch process, you need
 to look at where the dispatch process is being done and on "who's"
 behalf.

 The language standard generic functions are not "jury-rigged" to
 follow this sort of protocol. Nor "third party" libraries.
 That doesn't preclude your own generic functions from following a protocol
 along these lines. Yes you had to do some of the grunt work yourself,
 but macros can probably carry most of that burden.

 There may very well be a better way of doing this.  Another approach
 would involve wrapping handlers around code to snag the "no applicable
 method errors" and resolve them somehow. I like the above approach better.

--

Lyman S. Taylor                 "Because no matter where you go,

                                                Buckaroo Banzai



Sat, 17 Mar 2001 03:00:00 GMT  
 Delegation


....

Quote:
>   It would seem that you can you do this with generic functions also....

>   define generic some-action  (  arg1 :: <object> , arg2 :: <object> ) ;

>   define method  some-action  (  arg1 :: <object> , arg2 :: <object> )
>   begin

P.S.  this default "least applicable method" and generic need not be
     specialized at the most abstract height of <object>.  However, it
     should be most abstract, yet appropriate class.

     If you want to emulation smalltalk and its no types on method args
     ways... it should be <object>. :-)

     There is also an underlying assumption in the protocol presented that
     specific types are used in method definitions.  Hence, the least
     applicable signature isn't being used elsewhere.

--

Lyman S. Taylor                 "Because no matter where you go,

                                                Buckaroo Banzai



Sat, 17 Mar 2001 03:00:00 GMT  
 Delegation

Quote:



>>In some other OO languages, notably Smalltalk and Objective-C, it is a
>>common idiom to delegate[*] messages that are sent to one object to
>>another object. The interesting thing is that this is not done with
>>individual forwarding methods, but rather with one catch-all method that
>>relays all messages that can't be handled by the original receiver to a
>>delegate object.

>   Isn't this about using the doesNotUnderstand mechanism...  if so
>   this got covered back in August...
>                                    ... but taking another stab at it.

[ka-snip]

Quote:
> There may very well be a better way of doing this.  Another approach
> would involve wrapping handlers around code to snag the "no applicable
> method errors" and resolve them somehow. I like the above approach better.

This brings up another question.

One could say that Obj-C has a delegate capability because Smalltalk has a
delegate capability. Now, Dylan doesn't have an inherent delegate capability
the way that Smalltalk does. I guess the real question is "Does Dylan NEED a
delegate capability?". The August thread came about, I think, because
someone was trying to leverage Smalltalk idioms on to Dylan.

But, CLOS, being very similiar to Dylan, doesn't have the delegate ability
either, yet it's managed to "get by" without it.

Now, if you were trying to port Smalltalk code into Dylan, you'd probably
want good support for delegation, as Smalltalk seems to utilize it quite a
bit.

But, what is Smalltalk doing that NEEDS delegation? And how would one do it
in Dylan instead? Perhaps delegation is a hack to get around
multiple-inheritance, or some other limitation in Smalltalk. (Not implying
that Dylan has no limitations -- for example, it's delegation is just
terrible! :-)). It just seems that if you had Project A, and two teams, X
and Y, if one team implemented in Smalltalk, then, perhaps, delegation would
be leveraged to solve the task, whereas the other Dylan team would have
solved the problem without even missing delegation. The object models of the
two languages are so different that completely different approaches are used
to implement the system and solve the same problem.

Is it fair to say that delegation isn't missing from Dylan because
delegation isn't necessary?

I'm just trying to get some clarity of delegations role. I'm not trying to
fire up a Dylan <=> Smalltalk battle.



Sun, 18 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

> Is it fair to say that delegation isn't missing from Dylan because
> delegation isn't necessary?

I think your statement is fair.

I'd see delegation as an idiom, not as a (present or missing) language feature.
Smalltalk programmers use delegation apparently in the form of an implementation
technique to sort of emulate multiple inheritance. This idiom was developed (I'm
having a guess here) to make other Smalltalk programmers believe he/she is
dealing with a multiple (implementation and interface) inheritance system - or
at least they can treat is as such. If you think of Java (as another example)
with its interface notion you see that the focus has shifted; Java has multiple
interface inheritance, but still code inheritance must be implemented by means
of delegation. C++ lacks memory management, so people came up with the
envelope-letter idiom that lets them treat objects as if they were
reference-counted. Dylan does not need all of that, so there is no need for any
such idiom.

HW
--
Phone: +41-1-2341111
Fax: +41-1-2364671

Web: http://www.ubs.com/ubilab



Sun, 18 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

>Is it fair to say that delegation isn't missing from Dylan because
>delegation isn't necessary?

I don't believe it's entirely fair to state that "delegation is missing
from Dylan." Neither it is entirely fair to state the "delegation is
present in SmallTalk." Delegation is an idiom, a way to using language
features to implement specific behavior in one's application.

Delegation can solve a number of different problems. As you point out, it
can be used to "get around" the lack of multiple inheritance in SmallTalk
(although I don't like that characterization, I can't say it is factually
incorrect). It can also be used to implement proxies or handles, another
interesting idiom.

So, I would say that delegation isn't necessary the same way that
multiple inheritance (or even object languages) is not _necessary_. They
are <coy>merely</coy> useful tools from the developer's tool bench.
Nonetheless, delegation is a powerful tool for solving some classes of
problems (the RPC problem for example).

My US$0.02.

Regards,
Eric

-----------------------------------------------------------------------
Eric Berdahl                "I don't have to take this abuse from you
Man Behind the Curtain       -- I've got hundreds of people waiting to
Intelligent Paradigm         abuse me."




Sun, 18 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

> I guess the real question is "Does Dylan NEED a
> delegate capability?".

Yes, I think so. Think of implementing FSMs. There's a common pattern to
do it in C++:

You have one abstract superclass State and concrete states State1,
State2, ... are derived from it. State itself understands all events
that any state can deal with and the concrete StateN classes override
the respective events/methods they apply to.

The FSM class proper keeps a pointer to an instance of State to which it
forwards all incoming events. When the state of the FSM changes the
pointer is changed to point to an instance of another concrete state
class.

If you have to code this by hand you have to write quite a number of
pretty boring classes and forwarding functions. Luckily there's a
compiler, SMC, that translates a tabular representation of transitions
in the appropriate C++ code (look around on <http://www.oma.com>).

I take it, that something similar to SMC could be done in Dylan with the
help of macros. It's only that I have a feeling that this way of
implementing an FSM isn't appropriate for Dylan. The truly dynamic way
would be to change the class of an existing object, but AFAIK that's not
possible. So, what's the right way to do it?

Michael

--
Michael Schuerig

http://www.schuerig.de/michael/



Sun, 18 Mar 2001 03:00:00 GMT  
 Delegation

....

Quote:

>You have one abstract superclass State and concrete states State1,
>State2, ... are derived from it. State itself understands all events
>that any state can deal with and the concrete StateN classes override
>the respective events/methods they apply to.

  It seems to me this can all be done in Dylan.  

  The state classes.  Generic functions
  for each one of these "events"  that take the state and apropriate args.

  The methods for concrete state classes added to the generic along with
  the defaults based on the abstract State.

define generic  state-eventN (  obj :: <state> ,  .... ) => () ;

define method   state-eventN (  obj :: <state> ,  .... )  => ()
    .. default action ..
end;

define method   state-eventN (  obj :: <stateN> , ... )  => ()
    .. specilized action ..
end;

Quote:
>The FSM class proper keeps a pointer to an instance of State to which it
>forwards all incoming events.

  This is where you loose me.  Admittedly my C++ is a tad rusty, but as
  far a I recall there are no class objects in C++.   This could be
  some "class variable" (static global specific to a class). Or an
  instance of the FSM class keeps a pointer to the an concrete instance
  of state.

  I assume the latter.

define class FSM
   slot   current-state :: <state> ;
    // could have another slot to cache or hold in a sequence all
    //   the states.   Otherwise GC'ed away if there no references
end class

  So I need another set of generic functions that just take the FSM for
  each event.   so for EventN

define generic eventN  (  obj :: <FSM> ...  ) => ();

define method eventN   ( obj :: <FSM> ...  ) => ()
   state-eventN ( current-state(obj) , obj , .... )
end;

Actually, if each state had a slot to the FSM it has a relationship to
you could collapse these "twin" generics (eventN and state-EventN ) into one.
The "define generic" should be on a shared superclass of <FSM> and <state>.
I think the circular class definitions work.

  define abstract class <FSM-event-receiver> ( <object> ) end ;

  define class <FSM> ( <FSM-event-receiver> )
    ... from before..
  end ;

  define  class <state> ( <FSM-event-receiver)
    slot  state-of :: <FSM>
  end ;

  define generic eventN  ( <FSM-event-receiver> ) => ();

  define method eventN  ( obj :: <FSM> , ... ) => ()
    eventN ( current-state(obj) , ... )
  end;

  ... and the  methods on state like defined on state-eventN above ...

You remove the need to have a seperate forwarding generic function and the
1 to 1 relationship between FSM and states is caputured in the class
definitions.

Quote:
> When the state of the FSM changes the
>pointer is changed to point to an instance of another concrete state
>class.

  The state-eventN methods are passed the FSM so they may mutate it as
  necessary.

Quote:
> ...  Luckily there's a
>compiler, SMC, that translates a tabular representation of transitions
>I take it, that something similar to SMC could be done in Dylan with the
>help of macros.

   Maybe not...  Dylan macros "mutate" Dylan syntax like expressions.  However,
   the above compiler is written in C to produce C++.  I imagine a Dylan
   program could written to produce Dylan. ;-)

Quote:
> It's only that I have a feeling that this way of
>implementing an FSM isn't appropriate for Dylan. The truly dynamic way
>would be to change the class of an existing object, but AFAIK that's not
>possible. So, what's the right way to do it?

  From my reading of this  problem description the dynamism in the C++ code
  is provided by the virtual member function calls.  Dylan's Generic
  Functions provide the same functionality.

  There is no need to "morph" one state into another.

--
Lyman S. Taylor            "emacs - ... Do NOT use vi to edit your programs.  

                                   edit/compile/debug cycle [with ] vi
                                    will make me despair of  your sanity..."
                                P. N. Hilfinger  CS 164 Fall '92 Syllabus



Sun, 18 Mar 2001 03:00:00 GMT  
 Delegation

Quote:
> I'd see delegation as an idiom, not as a (present or missing)
> language feature.

"Delegation" is used to refer to an idion.  But it is also used to
refer to certain language features.  We could say these are two
different meanings of "delegation", and when it looks like people
might be disagreeing about whether "delegation" is a language feature
or an idiom, they may just be using "delegation" in different ways.

Note that it does not follow from the existence of delegation language
fatures that a language lacking such features therefore lacks some
significant ability.  There will typically be some other way of doing
whatever was done with delegation, and though delegation may make some
things simpler, it will make others more complex; and when comparing
particular languages, it may be that for many purposes the language
with delegation is at a disadvantage.

For instance, T, a kind of Scheme, has a fairly simple object system
that lacks explicit classes but has a delegation feature called
"join".  If A and B are objects, the object (join A B) interprets
messages that are understood by A in the way that A would interpret
them, otherwise interpreting then as would B.  I happen to like the T
approach to objects, but I'd still say it's fairly clear that quite a
few things are more easily done in a language with rich, class-based
inheritance such as Dylan.

-- jd



Mon, 19 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

>You have one abstract superclass State and concrete states State1,
>State2, ... are derived from it. State itself understands all events
>that any state can deal with and the concrete StateN classes override
>the respective events/methods they apply to.

>The FSM class proper keeps a pointer to an instance of State to which it
>forwards all incoming events. When the state of the FSM changes the
>pointer is changed to point to an instance of another concrete state
>class.

In Dylan, methods and classes are not coupled. This makes solutions like
yours harder to implement, but lead to other, more natural solutions.
Especially mapping events to method calls looks inelegant in Dylan, in
sharp contrast to Smalltalk. This is because in Dylan there is no
receiver of a method call, the the notion of delegation is somewhat
inappropriate.

I used a completely different approach to implement state machines in Dylan,
making use of generic functions and multiple dispatch on singletons:

define method change-state ( old-state == #"state1", event == #"some-event" )
  => (new-state)
  // do side effects
  #"state2"; // return new state
end method change-state;

define method change-state ( old-state == #"state2", event == "other-event" )
  => (new-state)
  // side effects
  #"state1"; // new state
end method change-state

define method change-state ( old-state, event ) // this is the fallback
  => (new-state)
  error("Illegal event received");
end method change-state;

Of course one could use full-blown classes for events instead of symbols,
in case there's additional information to be embedded. Now the second part is
a <state-machine> class, which holds the state in a slot, and calls
change-state:

define class <state-machine> (<object>)
  slot state, init-value: #"state1";
end class <state-machine>

define method send-event ( sm :: <state-machine>, event )
  sm.state := change-state( sm.state, event );
end method send-event;

Further refinements could be that change-state is not called directly, but
using a slot in <state-machine> to hold a g.f. method, so the user can
select another function instead of change-state, or making send-event the
setter function of class.state, for user comfort.

Last, but not least, a macro would help in saving typing for all the
change-state methods, giving a more table-like look. This could look like
this:

define state-machine <simple-example>
  transition(#"state1", #"some-event") => #"state2"
    // some code
  end transition;
  transition(#"state2", #"other-event") => #"state1"
    // more code
  end transition;
  default
    error("Illegal event received")
  end default
end state-machine;

Implementing this macro is left as an exercise to the reader ;).

Andreas

--
Gwydion Dylan Development -> http://www.randomhacks.com/dylan
"We're fully buzzword-compliant."



Mon, 19 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

> In Dylan, methods and classes are not coupled. This makes solutions like
> yours harder to implement, but lead to other, more natural solutions.

Thanks. I was exactly looking for a solution that does not mimick the
C++ (or Smalltalk) way -- a "natural" solution.

There must be more cases where the natural solutions in Dylan diverge
markedly from other programming languages. Has anyone tried to collect
them?

Michael

--
Michael Schuerig

http://www.schuerig.de/michael/



Mon, 19 Mar 2001 03:00:00 GMT  
 Delegation

Quote:

> There must be more cases where the natural solutions in Dylan diverge
> markedly from other programming languages. Has anyone tried to collect
> them?

Take a look at the paper "On the Interaction of Object-Oriented Design
Patterns and Programming Languages"
(http://www.cis.ohio-state.edu/~gb/Papers/Patterns.html). It takes into
account several programming languages, but touches Dylan and CLOS (e.g.,
multimethods, meta classes, singletons). It's very interesting to read.

HW
--
Phone: +41-1-2348754
Fax: +41-1-2364671

Web: http://www.ubs.com/ubilab



Tue, 20 Mar 2001 03:00:00 GMT  
 Delegation

Quote:


> > There must be more cases where the natural solutions in Dylan diverge
> > markedly from other programming languages. Has anyone tried to collect
> > them?

> Take a look at the paper "On the Interaction of Object-Oriented Design
> Patterns and Programming Languages"
> (http://www.cis.ohio-state.edu/~gb/Papers/Patterns.html). It takes into
> account several programming languages, but touches Dylan and CLOS (e.g.,
> multimethods, meta classes, singletons). It's very interesting to read.

I had read that paper several weeks ago and now have looked at it again.
What irritates me somewhat is that the authors seem a little too
focussed on the GOF-patterns.  It seems that in terms of language
constructs Dylan is exactly what the authors are advocating.

The only additions to Dylan that I can currently think of are pattern
matching of method arguments (as is commonly done in functional
languages), support for DBC (has already been discussed in this group),
and -- without understanding much of it! -- facilities for
aspect-oriented programming. I'm not sure, though, if anything of this
would really be sensible.

Michael

--
Michael Schuerig

http://www.schuerig.de/michael/



Thu, 22 Mar 2001 03:00:00 GMT  
 Delegation

Quote:
>The only additions to Dylan that I can currently think of are pattern
>matching of method arguments (as is commonly done in functional
>languages), support for DBC (has already been discussed in this group),
>and -- without understanding much of it! -- facilities for
>aspect-oriented programming. I'm not sure, though, if anything of this
>would really be sensible.

I would be interested in hearing peoples approaches to using aspect-oriented
programming using Dylan. At the AOP website there is a language specific to
aspect oriented programming called AspectJ - it is a preprocessor for Java
that provides aop constructs and produces Java source code. I've only looked
into it recently (mainly working through the powerpoint tutorial) and it
looks interesting.

Could the Dylan macro system be used to produce similar things to the
'aspect' features provided by AspectJ?

For those unfamiliar with it, you can read about AspectJ and Aspect Oriented
Programming at: http://www.parc.xerox.com/spl/projects/aop/aspectj/

Chris.
--
http://www.cnd.co.nz/creatures
icq 8600051



Fri, 23 Mar 2001 03:00:00 GMT  
 
 [ 22 post ]  Go to page: [1] [2]

 Relevant Pages 

1. a cute hack -- delegation after doesNotUnderstand

2. Delegation and Self like things for Squeak

3. Delegation - Smalltalk Implementation?

4. Forwarding / Delegation

5. a cute hack -- delegation after doesNotUnderstand

6. a cute hack -- delegation after doesNotUnderstand

7. SUMMARY -- delegation after doesNotUnderstand:

8. Delegation-based inheritance

9. Question on inheritance vs. delegation

10. Timeout, delegation - request

11. Timeout, delegation - request

12. STDERR vs $stderr - delegation bug

 

 
Powered by phpBB® Forum Software