Multiple delegates, contextual delegates

angel andrey.gelman at gmail.com
Wed Jun 26 09:48:35 UTC 2019


On Monday, 24 June 2019 at 16:05:33 UTC, Aphex wrote:
> 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?


Indeed it looks like a library solution.
Its alleged usefulness has nothing to do with being a library as 
opposed to being a part of the core language. Normally, the core 
language implements stuff that is very hard or very ugly to 
implement in a library, and your proposal will look perfect in a 
library implementation.

Another question regarding the "-=" operator - a delegate removal.
How can you recognize the delegate that has to be removed ? Do 
you need to type in the delegate code, just to identify the 
candidate for removal ?


More information about the Digitalmars-d mailing list