www.digitalmars.com         C & C++   DMDScript  

D - arrays of delegates

reply "Pavel Minayev" <evilone omen.ru> writes:
An idea came to my mind. D allows operators to be applied to the entire
array, so, for example, foo[]++ should be equal to:

    for (uint i = 0; i < foo.length; i++)
        foo[i]++;

Now, if we just think of () as of "call delegate" operator (and obviously
it is), then foo[]() should be treated as:

    for (uint i = 0; i < foo.length; i++)
        foo[i]();

Seems logical, doesn't it? And makes it easy to have arrays of delegates
without clumsy wrappers:

    void delegate(int x, int y)[] onMouseMove;
    void mouseMoved(int x, int y) { onMouseMove[](x, y); }    // call 'em
all!

    wnd.onMouseMove ~= &handler1;
    wnd.onMouseMove ~= &handler2;
    ...

Thus beating C# into dust! =)
Apr 14 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Great thinking, Pavel!!!

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Apr 14 2002
prev sibling next sibling parent "Walter" <walter digitalmars.com> writes:
That is a neat idea.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:a9bpa5$12pe$1 digitaldaemon.com...
 An idea came to my mind. D allows operators to be applied to the entire
 array, so, for example, foo[]++ should be equal to:

     for (uint i = 0; i < foo.length; i++)
         foo[i]++;

 Now, if we just think of () as of "call delegate" operator (and obviously
 it is), then foo[]() should be treated as:

     for (uint i = 0; i < foo.length; i++)
         foo[i]();

 Seems logical, doesn't it? And makes it easy to have arrays of delegates
 without clumsy wrappers:

     void delegate(int x, int y)[] onMouseMove;
     void mouseMoved(int x, int y) { onMouseMove[](x, y); }    // call 'em
 all!

     wnd.onMouseMove ~= &handler1;
     wnd.onMouseMove ~= &handler2;
     ...

 Thus beating C# into dust! =)

Apr 14 2002
prev sibling next sibling parent reply Patrick Down <pat codemoon.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in
news:a9bpa5$12pe$1 digitaldaemon.com: 

 Now, if we just think of () as of "call delegate" operator (and
 obviously it is), then foo[]() should be treated as:
 
     for (uint i = 0; i < foo.length; i++)
         foo[i]();
 

Can we apply this to arrays of objects too? class Foo { void Bar() { } void Fred() { } } Foo[] arr; arr[].Bar(); // ?? Or perhaps... with(arr[]) { Bar(); Fred(); }
Apr 14 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Suddenly, when we least expect it, we have a convenient syntax for
"foreach" constructs. :)

Of course, we can't use the return code from it (unless all of the
return codes are packed into an array the same length as arr[]

Nor is it totally clear what happens during exceptions.  Perhaps we need
a meta-variable that would tell us which index we were working on when
the exception was thrown?

--
The Villagers are Online! http://villagersonline.com

.[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ]
.[ (a version.of(English).(precise.more)) is(possible) ]
?[ you want.to(help(develop(it))) ]
Apr 14 2002
parent reply Patrick Down <pat codemoon.com> writes:
Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote in 
news:3CBA03E6.BF5DB7FD deming-os.org:

 Suddenly, when we least expect it, we have a convenient syntax for
 "foreach" constructs. :)
 
 Of course, we can't use the return code from it (unless all of the
 return codes are packed into an array the same length as arr[]
 

Actually I see no reason why this shouldn't work. int[] a; Foo[] b; a[] = b[].Bar();
Apr 14 2002
parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Patrick Down wrote:

 Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote in
 news:3CBA03E6.BF5DB7FD deming-os.org:

 Suddenly, when we least expect it, we have a convenient syntax for
 "foreach" constructs. :)

 Of course, we can't use the return code from it (unless all of the
 return codes are packed into an array the same length as arr[]

Actually I see no reason why this shouldn't work. int[] a; Foo[] b; a[] = b[].Bar();

There would also have to be a spec on how to do arguments. Consider: int Fred(); b[].Baz(Fred()); does this expand to: for(int i=0; i<b.length; i++) b[i].Baz(Fred()); or { int temp = Fred(); for(int i=0; i<b.length; i++) b[i].Baz(temp); } The difference could be very significant if Fred() actually did something (some side effect), or if Fred() had a long running time. I would argue for the latter, but would be very uncomfortable with it if the community didn't generally agree that that was the "right" (i.e. the anticipated) spec. Few things are worse than having something you *think* works one way, but works the opposite... -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Apr 14 2002
parent reply Patrick Down <pat codemoon.com> writes:
Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote in
news:3CBA6047.B188929 deming-os.org: 

 Patrick Down wrote:
 
 Russ Lewis <spamhole-2001-07-16 deming-os.org> wrote in
 news:3CBA03E6.BF5DB7FD deming-os.org:

 Suddenly, when we least expect it, we have a convenient syntax for
 "foreach" constructs. :)

 Of course, we can't use the return code from it (unless all of the


int Fred(); b[].Baz(Fred()); does this expand to: for(int i=0; i<b.length; i++) b[i].Baz(Fred()); or { int temp = Fred(); for(int i=0; i<b.length; i++) b[i].Baz(temp); } The difference could be very significant if Fred() actually did something (some side effect), or if Fred() had a long running time. I would argue for the latter, but would be very uncomfortable with it if the community didn't generally agree that that was the "right" (i.e. the anticipated) spec. Few things are worse than having something you *think* works one way, but works the opposite...

I would argue for the former. :) If you adopt this method then the latter example can always be constructed like this... int temp = Fred(); b[].Baz(temp) but it's impossible to go the other way without explictily making the loop.
Apr 14 2002
parent reply "Pavel Minayev" <evilone omen.ru> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns91F1EBD9CDFFpatcodemooncom 63.105.9.61...

 I would argue for the former. :)  If you adopt this method then
 the latter example can always be constructed like this...

 int temp = Fred();
 b[].Baz(temp)

 but it's impossible to go the other way without explictily
 making the loop.

Where you want a loop, it's better to state it clearly by making a loop. However, when you write: b[].baz(fred()); You only see fred() once in the expression, so, IMO, it should be evaluated only once.
Apr 15 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Pavel Minayev wrote:

 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns91F1EBD9CDFFpatcodemooncom 63.105.9.61...

 I would argue for the former. :)  If you adopt this method then
 the latter example can always be constructed like this...

 int temp = Fred();
 b[].Baz(temp)

 but it's impossible to go the other way without explictily
 making the loop.

Where you want a loop, it's better to state it clearly by making a loop. However, when you write: b[].baz(fred()); You only see fred() once in the expression, so, IMO, it should be evaluated only once.

They both are convincing arguments. I guess that whatever the syntax is, it should match all other array operations. Does b[] += fred(); call fred() once or many times? -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Apr 15 2002
prev sibling parent reply Patrick Down <pat codemoon.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in news:a9e8cb$elj$1
 digitaldaemon.com:
 
 Where you want a loop, it's better to state it clearly by making
 a loop. However, when you write:
 
     b[].baz(fred());
 
 You only see fred() once in the expression, so, IMO, it should
 be evaluated only once.

Ok I've changed my mind. :) fred() should only be executed once. My problem is that I want some sort of foreach construct in D and was trying to make the vector syntax fit that role. For the other problem I'd rather have this. foreach(a in b[]) // foreach(a,b[]), foreach(a,b) ?? { a.baz(fred()); }
Apr 15 2002
next sibling parent Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Patrick Down wrote:

 foreach(a in b[]) // foreach(a,b[]), foreach(a,b) ??
 {
   a.baz(fred());
 }

The first syntax makes sense to me from my ksh programming...but it would seem to conflict with the normal use of "in" - to test for presence of a key. What about "of" ? foreach(a of b[]) { a.baz(fred()); } 'a' would automatically get its type from the underlying type of the array. -- The Villagers are Online! villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Apr 15 2002
prev sibling parent reply "Robert W. Cunningham" <rcunning acm.org> writes:
Patrick Down wrote:

 "Pavel Minayev" <evilone omen.ru> wrote in news:a9e8cb$elj$1
  digitaldaemon.com:
 Where you want a loop, it's better to state it clearly by making
 a loop. However, when you write:

     b[].baz(fred());

 You only see fred() once in the expression, so, IMO, it should
 be evaluated only once.

Ok I've changed my mind. :) fred() should only be executed once. My problem is that I want some sort of foreach construct in D and was trying to make the vector syntax fit that role.

Wouldn't it be simpler to restrict the kinds of arguments that delegates are allowed to have? For example, require that delegates must have only discrete pass-by-value parameters. Concerns about single versus multiple argument evaluation should then go away. Delegate arrays are a powerful tool, and any limits placed on them should be there only to make them more generally useful. Restricting the arguments would simply mean the programmer would have to write code to circumvent the restriction (pass a function pointer in the above example, and call it within each delegate), which makes the risk explicit in the code instead of implicit in the language. That's good programming practice and good language design. Don't make the risky stuff (like side effects) impossible. But don't make them too easy either. And don't muck things up by mandating special execution behavior (single versus multiple evaluation) for delegate arrays: That just makes using delegate arrays more complex. Who will remember the rules correctly every time? (Other than Pavel, of course!) The compiler won't be able to help much in such situations unless the restrictions can be easily flagged at compile time (and easily fixed). Let's not make things so complex that we'll need a lint for D! -BobC
Apr 15 2002
next sibling parent "Pavel Minayev" <evilone omen.ru> writes:
"Robert W. Cunningham" <rcunning acm.org> wrote in message
news:3CBB9E37.1B196FB6 acm.org...

 Wouldn't it be simpler to restrict the kinds of arguments that delegates
 are allowed to have?  For example, require that delegates must have only
 discrete pass-by-value parameters.  Concerns about single versus
 multiple argument evaluation should then go away.

No, it doesn't: foo[].bar(baz()); Don't forget that baz() might do something as well, as well as return different values each time it's called. So the behaviour of this code could depend on whethe baz() is called once or many times.
Apr 15 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Robert W. Cunningham" <rcunning acm.org> wrote in message
news:3CBB9E37.1B196FB6 acm.org...
 That's good programming practice and good language design.  Don't make
 the risky stuff (like side effects) impossible.  But don't make them too
 easy either.  And don't muck things up by mandating special execution
 behavior (single versus multiple evaluation) for delegate arrays:  That
 just makes using delegate arrays more complex.  Who will remember the
 rules correctly every time?

That's a point worth repeating. I'm willing to sacrifice some expressive power of the language to gain orthogonality, consistency, and clarity in understanding code written by others. Every special case rule added to the language substantially reduces this, making it harder to learn/remember the language, and harder to read other peoples' code. That's also why D uses C's operator priority and integral promotion rules verbatim - such rules are a mess, but programmers are so used to them that changing them would be like trying to get people to switch from QWERTY to Dvorak.
Apr 16 2002
parent Roland <rv ronetech.com> writes:
Walter a écrit :

 "Robert W. Cunningham" <rcunning acm.org> wrote in message
 news:3CBB9E37.1B196FB6 acm.org...
 That's good programming practice and good language design.  Don't make
 the risky stuff (like side effects) impossible.  But don't make them too
 easy either.  And don't muck things up by mandating special execution
 behavior (single versus multiple evaluation) for delegate arrays:  That
 just makes using delegate arrays more complex.  Who will remember the
 rules correctly every time?

That's a point worth repeating. I'm willing to sacrifice some expressive power of the language to gain orthogonality, consistency, and clarity in understanding code written by others. Every special case rule added to the language substantially reduces this, making it harder to learn/remember the language, and harder to read other peoples' code. That's also why D uses C's operator priority and integral promotion rules verbatim - such rules are a mess, but programmers are so used to them that changing them would be like trying to get people to switch from QWERTY to Dvorak.

Yes..i should have posted my last post (D and real macros) in chat.. It is not D phylosophy, but it is still interesting i hope. roland
Apr 16 2002
prev sibling parent "J. Daniel Smith" <j_daniel_smith HoTMaiL.com> writes:
Actually, this is exactly the behavior you get from C# when using the
"event" keyword.  See thread "More on Delegats" (in "D alpha 26") for sample
code comparision between C# and D.  Your proposal would make the D version
of Fire_MyEvent() look almost identical to the C# version:
      void Fire_MyEvent(int event_data) {
            for (int i=0; i<MyEvent.length; i++)
            {
                dg_t e = MyEvent[i];
                e(event_data);
            }
becomes
      void Fire_MyEvent(int event_data) {
            MyEvent[](event_data)
      }
which is virtually identical to the C# version
        public void Fire_MyEvent(int event_data) {
            MyEvent(event_data);
        }

   Dan

"Pavel Minayev" <evilone omen.ru> wrote in message
news:a9bpa5$12pe$1 digitaldaemon.com...
 An idea came to my mind. D allows operators to be applied to the entire
 array, so, for example, foo[]++ should be equal to:

     for (uint i = 0; i < foo.length; i++)
         foo[i]++;

 Now, if we just think of () as of "call delegate" operator (and obviously
 it is), then foo[]() should be treated as:

     for (uint i = 0; i < foo.length; i++)
         foo[i]();

 Seems logical, doesn't it? And makes it easy to have arrays of delegates
 without clumsy wrappers:

     void delegate(int x, int y)[] onMouseMove;
     void mouseMoved(int x, int y) { onMouseMove[](x, y); }    // call 'em
 all!

     wnd.onMouseMove ~= &handler1;
     wnd.onMouseMove ~= &handler2;
     ...

 Thus beating C# into dust! =)

Apr 15 2002