www.digitalmars.com         C & C++   DMDScript  

D - anonymous delegates

reply "Jeroen van Bemmel" <anonymous somewhere.com> writes:
Walter,

Can I do anonymous delegates? Something along the lines of

void someFunction( Collection c, int i )
{
...
     c.traverse( int delegate( Object x ) {
        x.doSomething();
        return i + x.getCount();
     } );
...
}

( again some restrictions here in not allowing 'traverse' to store a
reference to this local delegate )
Feb 28 2003
next sibling parent "Walter" <walter digitalmars.com> writes:
Yes!

"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message
news:b3o6t2$30mt$1 digitaldaemon.com...
 Walter,

 Can I do anonymous delegates? Something along the lines of

 void someFunction( Collection c, int i )
 {
 ...
      c.traverse( int delegate( Object x ) {
         x.doSomething();
         return i + x.getCount();
      } );
 ...
 }

 ( again some restrictions here in not allowing 'traverse' to store a
 reference to this local delegate )

Feb 28 2003
prev sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Jeroen van Bemmel wrote:
 void someFunction( Collection c, int i )
 {
 ...
      c.traverse( int delegate( Object x ) {
         x.doSomething();
         return i + x.getCount();
      } );
 ...
 }

Just reverse the delegate and the int keywords and it'll be hot to trot.
Feb 28 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:b3ohda$62a$2 digitaldaemon.com...
 Jeroen van Bemmel wrote:
 void someFunction( Collection c, int i )
 {
 ...
      c.traverse( int delegate( Object x ) {
         x.doSomething();
         return i + x.getCount();
      } );
 ...
 }

Just reverse the delegate and the int keywords and it'll be hot to trot.

WARNING .... the delegate references the parameter 'i', this is only safe to do if traverse does not store the delegate. it is not like a Java anon class (which can only access finals in the outer scope.). underneath the simple syntax waits a smoking gun.
Feb 28 2003
parent reply "Jeroen van Bemmel" <anonymous somewhere.com> writes:
 WARNING .... the delegate references the parameter 'i', this is only safe

 do if traverse does not store the delegate.
 it is not like a Java anon class (which can only access finals in the

 scope.).
 underneath the simple syntax waits a smoking gun.

See the ( comment ) on my original post, that's why I chose a method 'traverse' with semantics that are likely to use the delegate immediately, rather than storing it. 'setCallback' for example would have been bad
Feb 28 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message
news:b3oroo$dep$1 digitaldaemon.com...
 WARNING .... the delegate references the parameter 'i', this is only


 to
 do if traverse does not store the delegate.
 it is not like a Java anon class (which can only access finals in the

 scope.).
 underneath the simple syntax waits a smoking gun.

See the ( comment ) on my original post, that's why I chose a method 'traverse' with semantics that are likely to use the delegate immediately, rather than storing it. 'setCallback' for example would have been bad

delegates (being an object and a method) especially in a language with GC one would expect to be safe to hang onto for a bit. IMHO this is another example of D being lead by implementation and not by semantics. (the Object to which the delgate is attached could be a copy of the stack frame at the time of creation [might cause problems in a deaper nested func where you would need several stack frames copied]) just like the code I posted before (returning a slice from static array) this is just another way for the unwary to scrible on the stack yet again I am confused by the direction that D is progressing more features are being added and each adds more pit falls rather than removing them. to anyone who has used (dare I mention it) Perl, Java or Scheme might not expect the dangers that lurk within the use of inner functions as delegates. in 90% of the projects I've worked on robustness and ability to prove robustness where more important than speed, so I'm a bit anti features that would adversly effect potential changes to the code they call. in you example what would happen if someone implemented lazy traversal (hold the delegate and only process if the result was required). the beauty of GC'd lang's is you can (usually) without worrying about who hold what when and why.
Feb 28 2003
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message
news:b3p5fg$lj2$1 digitaldaemon.com...
 (the Object to which the delgate is attached could be a copy of the stack
 frame at the time of creation [might cause problems in a  deaper nested

 where you would need several stack frames copied])

The semantic downside of that is that the stack variables so captured cannot be modified in a way that the outer function can access, for example, see www.digitalmars.com/d/cpptod.html#closures
Feb 28 2003
parent "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Walter" <walter digitalmars.com> wrote in message
news:b3p6cc$m0j$1 digitaldaemon.com...
 "Mike Wynn" <mike.wynn l8night.co.uk> wrote in message
 news:b3p5fg$lj2$1 digitaldaemon.com...
 (the Object to which the delgate is attached could be a copy of the


 frame at the time of creation [might cause problems in a  deaper nested

 where you would need several stack frames copied])

The semantic downside of that is that the stack variables so captured

 be modified in a way that the outer function can access, for example, see
 www.digitalmars.com/d/cpptod.html#closures

then follow the Java approach use final and put the value into the heap. final int i; // becomes int * i = new int; this gives the same semantics, but without the pit falls, at the expence of a new, with a thread local memory block to allocate from this is not a great overhead, unless you using a delegate from a nested func within a recusive func that is recuring. or you are calling that function a lot (in which case the old phrase optimise your algorithm not your code, comes to mind). or allow something like class Collection { int[10] array; void apply(void delegate(int) fp) { for (int i = 0; i < array.length; i++) fp(array[i]); } } void func(Collection c) { int max = int.min; void comp_max(int i) { if (i > max) max = i; } // creation of delegate from comp_max captures stack frame at this point in the code c.apply(comp_max); max = com_max.max; // restore from captured frame } against the java way void func(Collection c) { final int max = int.min; // int * max = new int; *max = int.min; or C++ int & max = *new int; void comp_max(int i) { if (i > max) // i>*max max = i; // *max = i; } // creation of delegate from comp_max captures stack frame at this point in the code still, but its capturing // int * not int. c.apply(comp_max); foo = max; // foo = *max; }
Feb 28 2003
prev sibling next sibling parent Dan Liebgold <Dan_member pathlink.com> writes:
In article <b3p5fg$lj2$1 digitaldaemon.com>, Mike Wynn says...
just like the code I posted before (returning a slice from static array)
this is just another way for the unwary to scrible on the stack
yet again I am confused by the direction that D is progressing more features
are being added and each adds more pit falls rather than removing them. to
anyone who has used (dare I mention it) Perl, Java or Scheme might not
expect the dangers that lurk within the use of inner functions as delegates.
in 90% of the projects I've worked on robustness and ability to prove
robustness where more important than speed, so I'm a bit anti features that
would adversly effect potential changes to the code they call.

Yikes... well my initial enthusiasm for the idea has given way to dread. I must completely concur with Mike's assessment. Again, someone coming from a Lisp/Scheme background would be unpleasantly surprised by this sort of closure behavior. Even coming from a more C/C++ background -- the bug you might introduce is just too subtle and (seemingly) unrelated. It doesn't seem right for the language to contain this sort of built-in hole (and I don't believe a run time check would really be both sufficient and efficient enough). The design-by-contract features of D indicate a push for reliability that this sort of dynamic stack-based closures undermine. The method Lisp uses to ensure that both the current function and any delegates access the same copies of the local environment is to not keep the locals on the stack (the environment is heap allocated when the function is called). In theory, only functions which require closures will need to avoid using the stack... so performance can be maintained in the majority of cases. Dan
Feb 28 2003
prev sibling parent reply Burton Radons <loth users.sourceforge.net> writes:
Mike Wynn wrote:
 just like the code I posted before (returning a slice from static array)
 this is just another way for the unwary to scrible on the stack
 yet again I am confused by the direction that D is progressing more features
 are being added and each adds more pit falls rather than removing them. to
 anyone who has used (dare I mention it) Perl, Java or Scheme might not
 expect the dangers that lurk within the use of inner functions as delegates.
 in 90% of the projects I've worked on robustness and ability to prove
 robustness where more important than speed, so I'm a bit anti features that
 would adversly effect potential changes to the code they call.

This reminds me of an ancient joke. Guy in an appointment with his doctor says "Doc, it hurts when I do this." Doctor replies, "Well, don't do that."
Feb 28 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
lol.

but if your doc said that to you, you'd go somewhere else for a second
opinion.

"Burton Radons" <loth users.sourceforge.net> wrote in message
news:b3pb16$q7h$1 digitaldaemon.com...
 Mike Wynn wrote:
 just like the code I posted before (returning a slice from static array)
 this is just another way for the unwary to scrible on the stack
 yet again I am confused by the direction that D is progressing more


 are being added and each adds more pit falls rather than removing them.


 anyone who has used (dare I mention it) Perl, Java or Scheme might not
 expect the dangers that lurk within the use of inner functions as


 in 90% of the projects I've worked on robustness and ability to prove
 robustness where more important than speed, so I'm a bit anti features


 would adversly effect potential changes to the code they call.

This reminds me of an ancient joke. Guy in an appointment with his doctor says "Doc, it hurts when I do this." Doctor replies, "Well, don't do that."

Mar 01 2003
parent reply Burton Radons <loth users.sourceforge.net> writes:
[this might double-post]

Mike Wynn wrote:
 lol.
 
 but if your doc said that to you, you'd go somewhere else for a second
 opinion.
 
 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:b3pb16$q7h$1 digitaldaemon.com...
 
This reminds me of an ancient joke.  Guy in an appointment with his
doctor says "Doc, it hurts when I do this."  Doctor replies, "Well,
don't do that."


The point is that you're ignoring the difference between constant and aggravated pain. Those coming from another language where their prejudices are incompatible might try writing nested delegates incorrectly, but they'll get burned and adjust their practices to never do that again. When used properly, the feature as-is is helpful, and is applicable to a range of jobs that is not entirely a subset to full closures - I wouldn't be able to apply them as vigorously if they caused allocations.
Mar 01 2003
parent reply "Mike Wynn" <mike.wynn l8night.co.uk> writes:
"Burton Radons" <loth users.sourceforge.net> wrote in message
news:b3ri0n$2iq4$1 digitaldaemon.com...
 [this might double-post]

 Mike Wynn wrote:
 lol.

 but if your doc said that to you, you'd go somewhere else for a second
 opinion.

 "Burton Radons" <loth users.sourceforge.net> wrote in message
 news:b3pb16$q7h$1 digitaldaemon.com...

This reminds me of an ancient joke.  Guy in an appointment with his
doctor says "Doc, it hurts when I do this."  Doctor replies, "Well,
don't do that."


The point is that you're ignoring the difference between constant and aggravated pain. Those coming from another language where their prejudices are incompatible might try writing nested delegates incorrectly, but they'll get burned and adjust their practices to never do that again. When used properly, the feature as-is is helpful, and is applicable to a range of jobs that is not entirely a subset to full closures - I wouldn't be able to apply them as vigorously if they caused allocations.

I'm not ignoring it what conserns me is the way you get burned, a performance hit is one thing, even getting an incorrect value is not so bad as long as its at the place where you make the call, but rewriting the stack which might contain a return location, is something that ideally should not be possible (without doing something that is obviously abusive) as it can go off a long time after the call that caused it and can be hard to track down even with a good debugger. I don't deny that it is a powerful and useful feature (like array slicing) but like returning a slice from an array on the stack would you consider D allowing non virtual destructors as C++ does ? maybe I've got the wrong impession of what D is, I got the impression that it was a language with a C like syntax that would allow me to write a program if it compiled and I ran it and got the wrong result, then it was my algorythm that was wrong and not my coding if I tried to do something that was not safe then I'd get an assert or exception. what is needed is for the delegate to be marked as auto (i.e. refers [in someway] to an item on a parent stack frame) so it can only be used, or passed as a param (auto only), but never stored. as I said before my primary consern is robustness (and its proveabilty) and not raw performance, second is ability to produce stand alone applications that do not require any other libraries or runtimes (so .NET and Java are not applicable). which leaves C/C++ or delphi/Kylix delphi is a little more robust than C but lacks GC. I accept that I may be a robustness bigot but who wants a fast program that randomly crashes.
Mar 03 2003
parent "Walter" <walter digitalmars.com> writes:
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message
news:b40fkm$252h$1 digitaldaemon.com...
 what is needed is for the delegate to be marked as auto (i.e. refers [in
 someway] to an item on a parent stack frame) so it can only be used, or
 passed as a param (auto only), but never stored.

Actually, I have figured out a way to make a runtime check on that which will prevent erroneous use.
Mar 04 2003