Multiple delegates, contextual delegates

Aphex Aphex at mail.com
Mon Jun 24 16:05:33 UTC 2019


I believe it would really help D to have multiple delegates and 
contextual delegates.

Take, for example

alias Caller = void delegate();

class X
{
     Caller Do;
}


x.Do = () { };


The issue is that one cannot assign multiple functions to Do and 
easily call them all in sequence(or even parallel using tasks).

I have created a library solution but the problem is that I have 
to keep the library around. I believe it is a very useful tool to 
be able to contain multiple delegates in a single object 
reference because there are many applications where one wants too 
allow multiple behaviors attached to a single reference. E.g., it 
has applications in messaging, notifications, graphics, systems, 
etc.

It's true one can simply make an array of callbacks, but then one 
still has to manually fire them all, deal with nulls, etc.


Ideally something like

alias Caller = void mdelegate();

class X
{
     Caller Do;
}

x.Do = () { writeln("1"); };

x.Do = () { writeln("2"); };

x.Do = () { writeln("3"); };

x.Do();

then fires all the delegates(in sequence of course).  (One could 
use ~= or += for adding and -= for removing if one wants)

It is true that this can somewhat be effectively done in a 
library but it should be part of the standard library and 
implemented well. I think though that it is better part of the 
language itself because it is quite useful.

One could have different ways to fire the delegate such as 
sequential iterative(sorta emulating yield) or parallel(creates a 
task for each one).

By having mdelegate one can immediately convert a delegate in to 
a mutlidelegate by a search and replace of the term without any 
issues or modification of other code, except if = is used which 
might cause problems on reassignment. It may require slight 
modifications on assignment to make things work but a mdelegate 
can behave as a normal delegate otherwise.


Contextual delegates are simply delegates that are methods that 
also capture the context.




alias Caller = void cdelegate();

class X
{
     int z = 3;
     Caller Do;
}

int z = 4;
int y = 10;
x.Do = () { writeln(context.z*y); }; // context used to 
disambiguate and reference object(possibly could use this but a 
this may already exist)

x.Do();


Essentially it keeps two "this"'es. The this of the object also 
of the context.

It's true one can do this as


alias Caller = void cdelegate(X);

class X
{
     int z = 3;
     Caller Do;
}

int y = 10;
x.Do = (q) { writeln(q.z*y); };

x.Do(x);

It might be better called member delegates as they are hybrids of 
members and delegates.


While it doesn't seem like much work to create them in d, it is 
quite ugly in having to pass the object.


By having a cdelegate one immediately can convert delegates used 
in structs and classes in to more powerful objects with no other 
code change than a search and replace. Without it one has to find 
all occurrence of and modify it to work.



Of course, then one has cmdelegate! ;) A contextual multi 
delegate! Isn't generalization fun?

These features really take very little compiler magic. Multi 
delegates can be done in a library but contextual delegates 
cannot.  Both extend delegates very easily and without much 
trouble yet make them more powerful. They also can be combined to 
multiply the usefulness.

In fact, it may be possible to simply extend the above behavior 
to the keyword delegate without any ill-effect. It would be 100% 
backwards compatible. The only issue is dealing with the 
assignment of delegates since previous behavior is expected to 
overwrite while new behavior would be expected to append... and 
these are mutually exclusive. To keep things 100% backwards 
compatible would require assignment to overwrite which would turn 
a multi-delegate in to a single delegate... and appendage would 
turn a delegate in to a multi-delegate.

e.g.,

x.Do = () { }; // First, and so old school delegate.
x.Do = () { }; // Overwrites.
x.Do += () { }; // Added a new delegate and now we have a 
multi-delegate with 2 delegates

x.Do = () { }; // Back to a single delegate that overwrote the 
previous two.
x.Do += () { }; // Added a new delegate and now we have a 
multi-delegate with 2 delegates
x.Do -= () { }; // Back to a single delegate since we removed the 
old one(technically not since the reference must be correct)


So the only issue here is that using multi-delegates in 
pre-existing code may fail if one does not know about future 
assignment and the multi-delegates get overwritten. This 
generally won't be a problem because rarely does one assign to a 
delegate multiple times, but even so, it's just a matter of 
converting to the appender op.

The power these offer far outweigh the rare likelihood of 
unexpected behavior(one far more likely to have other more 
serious issues such as a typical buffer overflow).



What do you think?








More information about the Digitalmars-d mailing list