D - DMD 0.57 release
- Walter (2/2) Feb 25 2003 Some long overdue features.
- Russ Lewis (11/13) Feb 25 2003 Non C-style function pointer declaration?
- Mike Wynn (44/58) Feb 25 2003 I second that,
- Walter (13/51) Feb 25 2003 No. Both foo() and bar() are referring to the same instance of 'a', so
- Patrick Down (3/7) Feb 25 2003 Walter, you are the man!
- Jonathan Andrew (14/16) Feb 25 2003 Cool!
- Walter (6/16) Feb 25 2003 is
- Burton Radons (2/29) Feb 25 2003 Removing that would make properties impossible.
- Walter (3/6) Feb 25 2003 Please elaborate.
- Burton Radons (12/21) Feb 26 2003 You wouldn't be able to differentiate between a property access and a
- Walter (3/14) Feb 26 2003 Ah, I see what you mean.
- Burton Radons (59/59) Feb 25 2003 Excellent all around. You needn't thank me for berating you. :-)
- Walter (4/5) Feb 25 2003 Your idea of using the frame pointer as the hidden this pointer was pret...
- Dan Liebgold (39/41) Feb 25 2003 Kudos and many thanks for the new function/delegate types!
- Walter (11/11) Feb 25 2003 I knew there was a difference, but I didn't know the right terminology. ...
- Antti Sykari (16/21) Feb 25 2003 However, it would seem that lexical closures can be a source of
- Antti Sykari (40/61) Feb 25 2003 Actually, if you'll tolerate overly imaginative ideas...
- Dan Liebgold (11/13) Feb 26 2003 Again to clarify my earlier mistatement:
- Antti Sykari (25/40) Feb 26 2003 There seems to be quite a bit of confusion around this matter, and I
- Walter (3/7) Feb 26 2003 That's correct.
- Dan Liebgold (14/26) Feb 26 2003 True true. Dynamic scoping is what exists in C/C++/C#/Java, and is what...
- Walter (8/13) Feb 26 2003 priority that
- Walter (7/11) Feb 25 2003 you're
- Jeroen van Bemmel (13/13) Feb 25 2003 OK, I'm gonna play the devil's advocate some more now, hope you'll excus...
- Walter (7/9) Feb 25 2003 me
- Jeroen van Bemmel (13/13) Feb 25 2003 That's fast....
- Walter (18/31) Feb 25 2003 Also, nested functions aren't really an OO concept. But the example I us...
- Jeroen van Bemmel (25/28) Feb 26 2003 .. and then there's for example a (not "the") Java way:
- Walter (16/45) Feb 26 2003 No, it's just been hidden away inside the implementation of the get(id)
- Walter (50/56) Feb 26 2003 using
- Jeroen van Bemmel (30/52) Feb 27 2003 I won't say it's better, but in Java you can do more or less the same:
- Jeroen van Bemmel (9/9) Feb 27 2003 BTW, now that I'm thinking about this: One of the worse features of Java...
- Dan Liebgold (22/27) Feb 27 2003 and
- Walter (20/37) Feb 27 2003 be
- Jeroen van Bemmel (5/6) Feb 27 2003 The point is not that you wouldn't be able to make it work, the point is
- Walter (6/11) Feb 27 2003 function
- Sean L. Palmer (9/20) Mar 01 2003 What information would the compiler need to have in order to be able to
- Walter (5/7) Mar 01 2003 It would have to know that any function you passed the delegate to would...
- Jeroen van Bemmel (20/22) Mar 01 2003 No, but I believe it could be implemented as a compile-time check the wa...
- Burton Radons (8/10) Mar 01 2003 It would need to allocate an object at the start of the function that
- Dan Liebgold (9/23) Feb 27 2003 Ah.. clever approach, certainly. Do-it-yourself lexical closures, simil...
- Patrick Down (4/32) Feb 27 2003 No the pointer will be held in a delegate which is a object/function
- Walter (8/15) Feb 27 2003 I'm thinking it may be possible to add a runtime check for that.
- Jeroen van Bemmel (10/11) Feb 27 2003 It may even be possible to do static checking:
- Walter (6/17) Feb 27 2003 to
- Peter Hercek (9/20) Mar 01 2003 I think it can be done even without the "global" keyword, by analyzing t...
- Antti Sykari (150/161) Mar 01 2003 Okay, let's discuss. First, let's make the vocabulary clear. There
- Patrick Down (39/41) Mar 01 2003 7. Marking functions that will hold a reference.
- Antti Sykari (6/6) Mar 01 2003 A funny thought crossed my mind...
- Dan Liebgold (42/54) Feb 28 2003 Here's an idea for some syntactic sugar to make that pill easier to swal...
- Jeroen van Bemmel (15/23) Mar 01 2003 Talking about hidden and obscure implicit behavior!
- Peter Hercek (13/23) Mar 01 2003 When you need a lexical closures, you need to create an object for it
- Sean L. Palmer (5/8) Mar 01 2003 You're right... and you can't mark a user-defined cast as "explicit". I...
- Jeroen van Bemmel (30/33) Mar 02 2003 constructors
- Antti Sykari (12/33) Mar 02 2003 You probably meant vice versa:
- Sean L. Palmer (27/61) Mar 02 2003 Perhaps they should have chosen to make explicit behavior the default, a...
- Walter (15/24) Feb 27 2003 IMO
- Bill Cox (4/20) Feb 26 2003 Why does function nesting impact automatic in-lining? Is a nested
- Walter (4/13) Feb 26 2003 inline
- Andy Friesen (6/12) Feb 25 2003 The linker doesn't seem to like it when you put two function literals in...
- Walter (4/9) Feb 25 2003 I should fix that.
- Patrick Down (19/25) Feb 25 2003 e:\Programming\tools\dmd\bin\..\..\dm\bin\link.exe err,,,user32
-
Farmer
(19/19)
Feb 26 2003
"Walter"
wrote in - Burton Radons (9/9) Feb 27 2003 Function pointers don't print diagnostics using the new syntax, such as
- Burton Radons (15/15) Feb 27 2003 This one is longstanding.
- Patrick Down (3/5) Feb 27 2003 I'll vote for this too.
- Walter (4/17) Feb 27 2003 It already does.
- Burton Radons (4/11) Feb 27 2003 Ack, there're places in Phobos which assume that static has the C
- Peter Hercek (30/36) Mar 01 2003 Will this no-op change to "done"?
- Walter (10/41) Mar 01 2003 news:b3mnhp$26ii$1@digitaldaemon.com...
- Peter Hercek (2/14) Mar 01 2003 Got it! Thanks.
- Burton Radons (13/13) Feb 27 2003 Weird bug with static:
- Patrick Down (28/32) Feb 27 2003 The following program prints:
- Patrick Down (29/68) Feb 27 2003 Similar example. i and j are messed
- Walter (1/1) Feb 28 2003 They're both compiler bugs and I'll fix them. -Walter
- Burton Radons (17/17) Feb 27 2003 Now with nested and inline functions:
- Burton Radons (2/3) Feb 27 2003 Gah, I'm a stupidhead. Sorry.
- Burton Radons (19/19) Mar 02 2003 This requires multiple modules:
- Walter (4/23) Mar 02 2003 Hmmm. Looks like a compiler bug.
- Burton Radons (21/21) Mar 02 2003 Here's a symbol bug:
- Burton Radons (9/9) Mar 02 2003 Assertions don't use the file name assigned through #line. For example:
Some long overdue features. www.digitalmars.com/d/changelog.html
Feb 25 2003
Walter wrote:Some long overdue features. www.digitalmars.com/d/changelog.htmlNon C-style function pointer declaration? Function literals? Nested functions? Closures??? YAAAAAAAAAAAAAAAAAAAAAAAAAY!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- 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))) ]
Feb 25 2003
I second that, anon delegates do replace inner classes in most places (but I still like them). before I run into problems I just want to check something in your example of a nested function uses as a delegate A delegate can be set to a non-static nested function: int delegate() dg; void test() { int a = 7; int foo() { return a + 3; } dg = foo; int i = dg(); // i is set to 10 } I assume at the time dg = foo is executed foo is closed so changes to a do not effect the return value of foo also is the following valid int delegate() dg; dg test( int param ) { int a = param+1; int foo() { return a + 3; } return foo; } or will foo try to reference a value on the stack which will not exist when test returns or even worse int delegate() dg; dg test( int param ) { int a = param+1; int foo() { return a = a + 3; } return foo; } fyi: Java does have closures of a sort MyInferface foo( int param ) { final int a = param + 1; // is realy int[]a = new int [1]; a[0] = param+1; return new MyInterface() { int func() { return a = a + 3; } // class this$0 implements MyInterface { final int[] _a; this$0(int[] a0) { _a = a0; } // public int func() { return _a[0] = _a[0] + 3; } // } // return new this$0( a ); } "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message news:3E5B8A00.5E699634 deming-os.org...Walter wrote:Some long overdue features. www.digitalmars.com/d/changelog.htmlNon C-style function pointer declaration? Function literals? Nested functions? Closures??? YAAAAAAAAAAAAAAAAAAAAAAAAAY!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -- 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))) ]
Feb 25 2003
"Mike Wynn" <mike.wynn l8night.co.uk> wrote in message news:b3gj28$1bii$1 digitaldaemon.com...in your example of a nested function uses as a delegate A delegate can be set to a non-static nested function: int delegate() dg; void test() { int a = 7; int foo() { return a + 3; } dg = foo; int i = dg(); // i is set to 10 } I assume at the time dg = foo is executed foo is closed so changes to a do not effect the return value of fooNo. Both foo() and bar() are referring to the same instance of 'a', so either can read/write to it and affect the other. It works exactly as if foo() was passed &a, and the body of foo() was accessing it through that pointer.also is the following valid int delegate() dg; dg test( int param ) { int a = param+1; int foo() { return a + 3; } return foo; } or will foo try to reference a value on the stack which will not existwhentest returnsThat is invalid code. The reference to 'a' is invalid after test() returns. If you think about it as being implemented with pointers to 'a', the semantics will become clear.or even worse int delegate() dg; dg test( int param ) { int a = param+1; int foo() { return a = a + 3; } return foo; }That's invalid too for the same reason. As Dan pointed out, D implements lexical closures, not semantic closures.fyi: Java does have closures of a sort MyInferface foo( int param ) { final int a = param + 1; // is realy int[]a = new int [1]; a[0] = param+1; return new MyInterface() { int func() { return a = a + 3; } // class this$0 implements MyInterface { final int[] _a; this$0(int[] a0) { _a = a0; } // public int func() { return _a[0] = _a[0] + 3; } // } // return new this$0( a ); }I'm not sure if those could be considered closures or not.
Feb 25 2003
"Walter" <walter digitalmars.com> wrote in news:b3fdlo$kp1$1 digitaldaemon.com:Some long overdue features. www.digitalmars.com/d/changelog.htmlWalter, you are the man!
Feb 25 2003
In article <b3fdlo$kp1$1 digitaldaemon.com>, Walter says...Some long overdue features. www.digitalmars.com/d/changelog.htmlCool! Quick question: In the delegates section under types, a function pointer is shown as: int function(int) fp; //function pointer int func(int); fp = &func; //now points to func whereas, in the functions section under closures, the assignment is shown as: fp = foo; Is one of these incorrect? If so, just for the record I like not needing the address-of operator. Thanks, -Jon
Feb 25 2003
"Jonathan Andrew" <Jonathan_member pathlink.com> wrote in message news:b3g944$14k4$1 digitaldaemon.com...Quick question: In the delegates section under types, a function pointerisshown as: int function(int) fp; //function pointer int func(int); fp = &func; //now points to func whereas, in the functions section under closures, the assignment is shown as: fp = foo; Is one of these incorrect? If so, just for the record I like not needingtheaddress-of operator.In earlier versions, the & was required. In 0.57, either will work. I'm thinking of obsoleting the & version.
Feb 25 2003
Walter wrote:"Jonathan Andrew" <Jonathan_member pathlink.com> wrote in message news:b3g944$14k4$1 digitaldaemon.com...Removing that would make properties impossible.Quick question: In the delegates section under types, a function pointerisshown as: int function(int) fp; //function pointer int func(int); fp = &func; //now points to func whereas, in the functions section under closures, the assignment is shown as: fp = foo; Is one of these incorrect? If so, just for the record I like not needingtheaddress-of operator.In earlier versions, the & was required. In 0.57, either will work. I'm thinking of obsoleting the & version.
Feb 25 2003
"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3gis9$1bf0$1 digitaldaemon.com...Please elaborate.In earlier versions, the & was required. In 0.57, either will work. I'm thinking of obsoleting the & version.Removing that would make properties impossible.
Feb 25 2003
Walter wrote:"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3gis9$1bf0$1 digitaldaemon.com...You wouldn't be able to differentiate between a property access and a function pointer access. For example: class Class { void *method () { } } Class object = new Class; void *pointer = object.method; Am I getting the pointer to the method or the pointer from the method? This decision could be deferred to the semantic phase, but that's pretty late to be making such an important decision on what code is representing.Please elaborate.In earlier versions, the & was required. In 0.57, either will work. I'm thinking of obsoleting the & version.Removing that would make properties impossible.
Feb 26 2003
"Burton Radons" <loth users.sourceforge.net> wrote in message news:3E5D5FAD.80605 users.sourceforge.net...You wouldn't be able to differentiate between a property access and a function pointer access. For example: class Class { void *method () { } } Class object = new Class; void *pointer = object.method; Am I getting the pointer to the method or the pointer from the method? This decision could be deferred to the semantic phase, but that's pretty late to be making such an important decision on what code is representing.Ah, I see what you mean.
Feb 26 2003
Excellent all around. You needn't thank me for berating you. :-) Here's a couple problems: void delegate () delegate (int x) store; void delegate () zoom (int x) { return delegate void () { }; } void main () { store = &zoom; } Which is to say that I'm attempting to assign an incorrect type to "store"; I get: Assertion failure: '!ident' on line 1854 in file 'mtype.c' Also: void delegate () delegate (int x) store; void delegate () zoom (int x) { return delegate void () { }; } void main () { store = &zoom; store () (); } Where I'm not passing an argument to the delegate in main, I get "Error: expected 1 arguments to constructor, not 0"; calling "zoom" directly with the same error results in the proper "function zoom (int x) does not match argument types ()". Finally: void main () { { const int [] list = [ 1, 2 ]; } { const int [] list = [ 3, 4 ]; } } Each "list" is in its own scope, but it fails with a "Previous Definition Different" error in linking. While I'm dealing with scopes: void main () { while (1) { int x; } while (1) { int x; } } Fails with "declaration main.x is already defined"; this is long standing. Finally: void main () { int x; for (int x; ; ) { } } Compiles, even though the "for" loop covers the previous variable; this is also long standing.
Feb 25 2003
"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3gavs$15ol$1 digitaldaemon.com...Excellent all around. You needn't thank me for berating you. :-)Your idea of using the frame pointer as the hidden this pointer was pretty cool.
Feb 25 2003
Kudos and many thanks for the new function/delegate types! Some notes: Especially for marketing purposes, you may want to refer to D's closures as "lexical closures", as most closures I've seen implented currently are "semantic closures" -- or "real" closures if you listen to the academics. This is an old dead battle in the Lisp community. Essentially, semantic closures are those that are described in the Vault language description (there are a few links in previous posts). Lisp and Scheme also implement them. They are "full" closures in that when a function is defined it fully captures its enclosing scope, such that even variables on the stack are retained for later reference. The lexical closures are the more implementationally sane version, which only captures the scope that exists anyway at the moment the function is called later. So in the documentation's example: int delegate () dg; void test() { int a = 7; int foo() { return a + 3; } dg = foo; } void bar() { test(); int i = dg(); // error, test.a no longer exists return; } In semantic closure, there would be no error. The definition of "foo" would invisibily capture the scope of "test()" in a garbage collected block, so that when it is called later through the dg delegate, "a" exists and is still valid. Semantic closures can play havoc with memory and performance. The scope retention happens invisibly, and can keep otherwise gc-able memory tied up. The implementation is also complex. I believe these are reasons you don't see them usage is atrocious (even the mature commercial environments) and its garbage collection is often a joke. Dan p.s. I don't want to start a Lisp flame war, so I take it all back if you're offended! Also, I use Lisp (again, a commercial version) daily so I'm not (necessarily) speaking from ignorance.... In article <b3fdlo$kp1$1 digitaldaemon.com>, Walter says...Some long overdue features. www.digitalmars.com/d/changelog.html
Feb 25 2003
I knew there was a difference, but I didn't know the right terminology. Now that I know it, I'll update the documentation. Semantic closure, at least in a C-style language, just does not feel intuitive to me. The vault style closures, especially the example they give on what values the captured variables have, to me just seems mighty peculiar. Implementing them is also obviously far more complex and expensive, and doesn't look worthwhile for something that is just confusing anyway. The lexical closure implementation turns out to be reasonably simple, easy to understand, and efficiently implementable. Thanks, -Walter
Feb 25 2003
"Walter" <walter digitalmars.com> writes:I knew there was a difference, but I didn't know the right terminology. Now that I know it, I'll update the documentation. Semantic closure, at least in a C-style language, just does not feel intuitive to me. The vault style closures, especially the example they give on what values the captured variables have, to me just seems mighty peculiar. Implementing them is alsoHowever, it would seem that lexical closures can be a source of annoying bugs, since they contain implicit pointers to the stack frame. So beware of introducing them to beginners before they know and understand what the stack is ;) They're a useful feature, anyhow. Probably some kind of semantic closures could be feasible, even in a language that allows allocating local variables on the stack: - creating a semantic closure would require allocating space for and making a copy of each reference to each heap-allocated object that is used inside the closure - however, stack-allocated objects could not be referred to, since they will be lost when the stack frame goes away -> yet another difference between integral/struct and class objects, or more appropriately, stack-allocated/heap-allocated objects -Antti
Feb 25 2003
Actually, if you'll tolerate overly imaginative ideas... One solution would be to make semantic closures the default. This would mean, naturally, that part of the local variables would have to be heap-allocated. void delegate() f() { int x; // <------ allocated from the heap, since it's needed // in the closure Object y; // <----- allocated from the heap anyway int z; // <------ stack-allocated because not referred by closures x = 5; y = new Whatever(6); z = 7; void foo() { print(x); print(y); } return foo; } Now, before someone yells, "that's inefficient", consider this: If the compiler could prove (or the programmer could assert) that f's lifetime will be longer than foo's, then it could implement above as lexical closure and stack-allocate everything. But this would be an optimization, not the default method. This would mean, though, that the semantics of X x, X y; x = y; would *have* be the same whether x is heap-allocated and stack-allocated. Possibly needs to have different "value-assignment" and "reference-assignment" operators, or something similar. (Oops, starting to sound like Eiffel?) I'm not sure how this would be achieved. Anyway, lexical closures by default are unsafe. I'm not someone to decide it's best to make the unsafe but efficient feature the default (like the C/C++-programming world feels), or if it's better to stay on the safe side by default and implement stack allocation etc. as an optimization (like the functional programming world seems to feel). It's up to the language designed. But having both optionally would be nice. -Antti Antti Sykari <jsykari gamma.hut.fi> writes:"Walter" <walter digitalmars.com> writes:I knew there was a difference, but I didn't know the right terminology. Now that I know it, I'll update the documentation. Semantic closure, at least in a C-style language, just does not feel intuitive to me. The vault style closures, especially the example they give on what values the captured variables have, to me just seems mighty peculiar. Implementing them is alsoHowever, it would seem that lexical closures can be a source of annoying bugs, since they contain implicit pointers to the stack frame. So beware of introducing them to beginners before they know and understand what the stack is ;) They're a useful feature, anyhow. Probably some kind of semantic closures could be feasible, even in a language that allows allocating local variables on the stack: - creating a semantic closure would require allocating space for and making a copy of each reference to each heap-allocated object that is used inside the closure - however, stack-allocated objects could not be referred to, since they will be lost when the stack frame goes away -> yet another difference between integral/struct and class objects, or more appropriately, stack-allocated/heap-allocated objects -Antti
Feb 25 2003
Again to clarify my earlier mistatement: What D implements is called a "dynamic closure", and the Lisp/Scheme closure which does capture locals on the heap is actually the "lexical closure". Those names make more sense anyway. Its called lexical because it preserves the lexical scope, or the scope as the source code would indicate, regardless of execution context. Dan "Antti Sykari" <jsykari gamma.hut.fi> wrote in message news:87lm03ldoy.fsf hoastest1-8c.hoasnet.inet.fi...Actually, if you'll tolerate overly imaginative ideas... One solution would be to make semantic closures the default.[...]
Feb 26 2003
"Dan Liebgold" <dliebgold yahoo.com> writes:Again to clarify my earlier mistatement: What D implements is called a "dynamic closure", and the Lisp/Scheme closure which does capture locals on the heap is actually the "lexical closure". Those names make more sense anyway. Its called lexical because it preserves the lexical scope, or the scope as the source code would indicate, regardless of execution context.There seems to be quite a bit of confusion around this matter, and I fell to the trap myself. Probably everything I said seems unintelligible because of the vocabulary. :) I don't think closures cannot be called dynamic or lexical. The distiction is between dynamic scoping and lexical scoping. In dynamic scoping, a variable refers to its last binding. In lexical scoping, the variable refers the binding which is nearest to it lexically. Lisp dialects used dynamic scoping and changed to lexical scoping (with the exception of elisp). http://www.gnu.org/manual/elisp-manual-21-2.8/html_node/elisp_147.html Closures (as used in Common Lisp, Scheme, and other functional languages, and languages like Perl, I think) capture the environment which contains the variables referred to. You need closures to implement lexical scoping. You don't need closured to implement dynamic scoping. (For example, http://www.gnu.org/manual/elisp-manual-21-2.8/html_node/elisp_150.html#SEC150 ) D's closures/delegates look like they don't actually capture the variables (in the sense that they remain in existence after the block in which they have been declared in has exit) but merely refer to them. -AnttiDan "Antti Sykari" <jsykari gamma.hut.fi> wrote in message news:87lm03ldoy.fsf hoastest1-8c.hoasnet.inet.fi...Actually, if you'll tolerate overly imaginative ideas... One solution would be to make semantic closures the default.[...]
Feb 26 2003
"Antti Sykari" <jsykari gamma.hut.fi> wrote in message news:878yw3e5h3.fsf hoastest1-8c.hoasnet.inet.fi...D's closures/delegates look like they don't actually capture the variables (in the sense that they remain in existence after the block in which they have been declared in has exit) but merely refer to them.That's correct.
Feb 26 2003
In article <878yw3e5h3.fsf hoastest1-8c.hoasnet.inet.fi>, Antti Sykari says...The distiction is between dynamic scoping and lexical scoping. In dynamic scoping, a variable refers to its last binding. In lexical scoping, the variable refers the binding which is nearest to it lexically. Lisp dialects used dynamic scoping and changed to lexical scoping (with the exception of elisp).Good, I'm not totally botching these descriptions ;)You need closures to implement lexical scoping. You don't need closured to implement dynamic scoping.programmers *expect*, and is straightfoward to implement.D's closures/delegates look like they don't actually capture the variables (in the sense that they remain in existence after the block in which they have been declared in has exit) but merely refer to them.Yes, D has dynamic scoping. Lexical scoping opens up possibilities for different (often more powerful) styles of programming, but like a few of the other features of Lisp and its ilk, it is rarely implemented in an optimal way. So while it has a theoretic performance that is in the league of the C/C++ paradigm but you will rarely encounter it in the field. D, it seems, is designed for practical performance to be of higher priority that theoretically powerful constructs that complicate implementation, like lexical scoping. BTW, I believe that is the ultimate downfall of first class functions. Dan
Feb 26 2003
"Dan Liebgold" <Dan_member pathlink.com> wrote in message news:b3j9p5$39b$1 digitaldaemon.com...D, it seems, is designed for practical performance to be of higherpriority thattheoretically powerful constructs that complicate implementation, likelexicalscoping.Correct. Also, D tries to avoid features that are overly complex to implement.BTW, I believe that is the ultimate downfall of first class functions.Implementing lexical closure would kill off the uses of nested functions in the examples I used, because the performance would be unacceptable.
Feb 26 2003
"Dan Liebgold" <Dan_member pathlink.com> wrote in message news:b3ghmn$1al3$1 digitaldaemon.com...p.s. I don't want to start a Lisp flame war, so I take it all back ifyou'reoffended!Not at all! I'm in the wrong business if I'm easilly offended!Also, I use Lisp (again, a commercial version) daily so I'm not (necessarily) speaking from ignorance....And that makes your comments on it especially worth reading. My knowledge of Lisp is minimal, and I've written little more in it than a few emacs functions.
Feb 25 2003
OK, I'm gonna play the devil's advocate some more now, hope you'll excuse me for that, but...why is it useful to be able to declare nested functions? The reason for having general functions / procedures is to have the ability to break up a problem into smaller subproblems, and then solving each subproblem which is easier to do. You continue to do so until you arrive at problems that can be solved by single statements or machine instructions. Basic divide-and-conquer Nested functions are normal functions with additional scoping restrictions. Do they allow you to better match the structure of a problem? Could it be a matter of taste perhaps? I don't see a real use case, and since I am sort of a minimalist I would leave them out unless there is a good reason to put them in. Yes, I believe you can implement them, even efficiently. But being able to jump off a cliff doesn't mean that you have to...
Feb 25 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3grne$1hat$1 digitaldaemon.com...OK, I'm gonna play the devil's advocate some more now, hope you'll excusemefor that, but...why is it useful to be able to declare nested functions?Speak of the devil <g> I just wrote this example: www.digitalmars.com/d/pretod.html#codefactoring The C version is done with macros, but if you want, try it with C++ inline functions.
Feb 25 2003
That's fast.... I can see your point when comparing it to old-style C macro's, no argument there. However, in the "OO world" ( many discussions on OO vs functional aside ) you would probably introduce 'stack' as an abstract data type (it might already be in your runtime libary under, say, lang.util...) and encapsulate all those little functions there. Agreed, it's a matter of taste/hype/era, a different cross section of the problem domain. But still, thinking also in terms of reuse, if you end up with a couple of those inline functions you'll probably find yourself declaring them time and time again ( the push() and pop() in your example are most likely not the only place in the program where they are used, JVMs tend to push and pop a lot) until you finally decide to factor them out to only have to declare them once... So, resuming, nested functions are one way of factoring, there are others
Feb 25 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3guds$1ja3$1 digitaldaemon.com...I can see your point when comparing it to old-style C macro's, no argument there. However, in the "OO world" ( many discussions on OO vs functional aside ) you would probably introduce 'stack' as an abstract data type (it might already be in your runtime libary under, say, lang.util...) and encapsulate all those little functions there. Agreed, it's a matter of taste/hype/era, a different cross section of the problem domain.Also, nested functions aren't really an OO concept. But the example I used is a (greatly simplified) example of production quality code I've encountered more than once. The nested function route has the great advantage of resulting in code that is every bit as efficient as the C macro technique - there's no compromise.But still, thinking also in terms of reuse, if you end up with a couple of thoseinlinefunctions you'll probably find yourself declaring them time and time again ( the push() and pop() in your example are most likely not the only placeinthe program where they are used, JVMs tend to push and pop a lot) untilyoufinally decide to factor them out to only have to declare them once...Inline functions suffer from the same problem of lack of access to local variables in the enclosing scope - you need to create a context struct and pass that as a pointer. See the next example www.digitalmars.com/d/ctod.html#traversal which is a (again, greatly simplified) piece of code from my own work.So, resuming, nested functions are one way of factoring, there are othersSure. But nested functions are a simple and direct way of doing it without using pointers or creating extra wrapper object types just to encapsulate/transmit them.
Feb 25 2003
See the next example www.digitalmars.com/d/ctod.html#traversal which is a (again, greatly simplified) piece of code from my own work... and then there's for example a (not "the") Java way: static void findMember( Map[] symtables, String id ) { for (int s=0; s<symtables.length; ++s) { Symbol s = (Symbol) symtables[s].get(id); if (s!=null) { // do whatever you want here } } } No need for any inline functions or nested functions or whatever other functions :) I'm sorry, but the days for programming recursive pointer traversal algorithms have long been over for Java programmers. I'm using Java as an example here, I'm sure there are other languages that can do the same. Regardless of whether you like Java or not, if possible it would be nice to have examples that compare D features with state-of-the-art options in other languages. The fact that they occur in production code doesn't automatically mean they are a good (I won't say 'the right', there are always trade-offs ) approach As an objective person looking at D, I would be looking for features that help me solve problems that are not addressed adequately by what I am using today. Less lines of code, better maintainable code, better runtime support, "if you do it like this you automagically get <nifty feature>", all of these can be reasons. Until now the examples provided have not been convincing to me.
Feb 26 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3j9no$38n$1 digitaldaemon.com...No, it's just been hidden away inside the implementation of the get(id) function, and *that* code will need some sort of context pointer. Consider also the case of applying an arbitrary function to each member of the table (see the example in www.digitalmars.com/d/cpptod.html).See the next example www.digitalmars.com/d/ctod.html#traversal which is a (again, greatly simplified) piece of code from my own work... and then there's for example a (not "the") Java way: static void findMember( Map[] symtables, String id ) { for (int s=0; s<symtables.length; ++s) { Symbol s = (Symbol) symtables[s].get(id); if (s!=null) { // do whatever you want here } } } No need for any inline functions or nested functions or whatever other functions :) I'm sorry, but the days for programming recursive pointer traversal algorithms have long been over for Java programmers.I'm using Java as an example here, I'm sure there are other languages that can do the same. Regardless of whether you like Java or not, if possible it would be nicetohave examples that compare D features with state-of-the-art options inotherlanguages. The fact that they occur in production code doesn'tautomaticallymean they are a good (I won't say 'the right', there are alwaystrade-offs )approach As an objective person looking at D, I would be looking for features that help me solve problems that are not addressed adequately by what I amusingtoday. Less lines of code, better maintainable code, better runtimesupport,"if you do it like this you automagically get <nifty feature>", all ofthesecan be reasons. Until now the examples provided have not been convincingtome.Ok, how in Java do you apply an arbitrary piece of code to all the elements in some generic container?
Feb 26 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3j9no$38n$1 digitaldaemon.com...As an objective person looking at D, I would be looking for features that help me solve problems that are not addressed adequately by what I amusingtoday. Less lines of code, better maintainable code, better runtimesupport,"if you do it like this you automagically get <nifty feature>", all ofthesecan be reasons. Until now the examples provided have not been convincingtome.Here's another one. Suppose we have in our generic library a function to perform numerical integration: ------------------------------------------ // Integrate f(x) from x1..x2 by dx real integrate(real delegate(real x) f, real x1, real x2, real dx) { real sum = 0.0; for (real x = x1; x < x2; x += dx) { real y1, y2; y1 = f(x); y2 = f(x + dx); sum += (y1 + y2) * dx / 2.0; } return sum; } --------------------------------------------- Then, in our application code, we want to integrate from 0..x a polynomial of the form: f(x) = a + b*x + c*x**2 + ... Here's a function to do it with coeff[] representing the a, b, c coefficients: ----------------------------------------------- real integrate_polynomial(real coeff[], real x) { real f(real x) { real y = 0.0; real xp = 1.0; for (int i = 0; i < coeff.length; i++) { y += coeff[i] * xp; xp *= x; } return y; } return integrate(f, 0, x, .01); } ---------------------------------------- Notice how the function f(x) can be written to arbitrarilly reference other data in a typesafe manner, without any need to modify the generic library routine. numerical improvements, which isn't the point here.)
Feb 26 2003
----------------------------------------------- real integrate_polynomial(real coeff[], real x) { real f(real x) { real y = 0.0; real xp = 1.0; for (int i = 0; i < coeff.length; i++) { y += coeff[i] * xp; xp *= x; } return y; } return integrate(f, 0, x, .01); } ---------------------------------------- Notice how the function f(x) can be written to arbitrarilly referenceotherdata in a typesafe manner, without any need to modify the generic library routine.(Overlookingnumerical improvements, which isn't the point here.)I won't say it's better, but in Java you can do more or less the same: real integrate_polynomial( final real coeff[], real x) { Fuction f = new Function() { public int evaluate( real x ) { real y = 0.0; real xp = 1.0; for (int i = 0; i < coeff.length; i++) { y += coeff[i] * xp; xp *= x; } return y; } }; return integrate(f, 0, x, .01); } Note the 'final' to make coeff accessible. Alternatively, I could pass it as an argument to the constructor of f, but then I would need to define it as a datamember too. That is, you create a function object implementing some 'Function' interface which has an 'evaluate' method. One possible advantage here is that the D program you wrote only supports single argument functions f(x). If I wanted to also have f(x,y), I could add this to the interface, whereas in D it would have to be a different delegate (-parameter). In this sense I think interfaces (and anonymous implementations as shown above) are more generic that function pointers / delegates
Feb 27 2003
BTW, now that I'm thinking about this: One of the worse features of Java IMO is the rediculous amount of resources (memory) it uses for simple things. The garbage collector is good, but it allocates so many small objects (the example I gave allocates one for the function object). The advantage is though that I can return this object from my function, and use it elsewhere. What happens if I return a reference to a nested function delegate in D? Is it still valid, is there also some memory allocated underneath? You would expect it to go out of scope, so the compiler should issue a warning "returning reference to local function" or something
Feb 27 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3kh99$qoj$1 digitaldaemon.com...The advantage is though that I can return this object from my function,anduse it elsewhere. What happens if I return a reference to a nestedfunctiondelegate in D? Is it still valid, is there also some memory allocated underneath? You would expect it to go out of scope, so the compiler should issue a warning "returning reference to local function" or somethingThat's the rub. The nested function has static scope, so it will always be accesible and callable. It's enclosing environment is the problem. Local variables on the stack in its enclosing environment will become invalid when the surrounding function exits, so it's caller beware. An example where the D version will get you (by letting you introduce a subtle runtime bug) is this (shamelessly stolen from the Vault page): void startLogging (string logFilename) { file_handle logfile = openFile(logFilename, "a"); void handleInterrupt (int interrupt) { writeMessage(interrupt, logfile); } registerInterruptHandler(12, handleInterrupt); } When "handleInterrupt" is called later, "startLogging" will have exited, and "logfile" will have fallen out of scope. It is a local stack variable, so it will point to what is likely to be invalid memory. Cue access violation or worse. Dan
Feb 27 2003
"Dan Liebgold" <dliebgold yahoo.com> wrote in message news:b3kj07$rnc$1 digitaldaemon.com...That's the rub. The nested function has static scope, so it will alwaysbeaccesible and callable. It's enclosing environment is the problem. Local variables on the stack in its enclosing environment will become invalidwhenthe surrounding function exits, so it's caller beware. An example where the D version will get you (by letting you introduce a subtle runtime bug) is this (shamelessly stolen from the Vault page): void startLogging (string logFilename) { file_handle logfile = openFile(logFilename, "a"); void handleInterrupt (int interrupt) { writeMessage(interrupt, logfile); } registerInterruptHandler(12, handleInterrupt); } When "handleInterrupt" is called later, "startLogging" will have exited,and"logfile" will have fallen out of scope. It is a local stack variable, soitwill point to what is likely to be invalid memory. Cue access violation or worse.Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class: void startLogging(string logFilename) { class Foo { file_handle logfile; void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } } Foo f = new Foo; f.logfile = openFile(logFilename, "a"); registerInterruptHandler(12, f.handleInterrupt); } It's a little more work, but not so bad.
Feb 27 2003
It's a little more work, but not so bad.The point is not that you wouldn't be able to make it work, the point is that you create a bug that is hard to find. This kind of use of nested functions should be detected and refused. It should be illegal to store a reference to a nested local function. In fact, the type of a nested function should not be 'delegate' but "non_copyable_delegate' - can you make this ?
Feb 27 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3ligi$1gno$1 digitaldaemon.com...The point is not that you wouldn't be able to make it work, the point is that you create a bug that is hard to find. This kind of use of nested functions should be detected and refused. It should be illegal to store a reference to a nested local function. In fact, the type of a nestedfunctionshould not be 'delegate' but "non_copyable_delegate' - can you make this ?I agree that as much as possible the compiler should detect and reject such usage as illegal. But it cannot do it 100%. Another possibility would be to add a runtime check.
Feb 27 2003
What information would the compiler need to have in order to be able to detect such usage 100%? Sean "Walter" <walter digitalmars.com> wrote in message news:b3lnk1$1jv2$1 digitaldaemon.com..."Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3ligi$1gno$1 digitaldaemon.com...aThe point is not that you wouldn't be able to make it work, the point is that you create a bug that is hard to find. This kind of use of nested functions should be detected and refused. It should be illegal to store?reference to a nested local function. In fact, the type of a nestedfunctionshould not be 'delegate' but "non_copyable_delegate' - can you make thisI agree that as much as possible the compiler should detect and rejectsuchusage as illegal. But it cannot do it 100%. Another possibility would betoadd a runtime check.
Mar 01 2003
"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message news:b3r11a$2aq1$1 digitaldaemon.com...What information would the compiler need to have in order to be able to detect such usage 100%?It would have to know that any function you passed the delegate to would not store it in some global data structure. I think that's not possible. But, I think a runtime check can be done.
Mar 01 2003
It would have to know that any function you passed the delegate to wouldnotstore it in some global data structure. I think that's not possible.No, but I believe it could be implemented as a compile-time check the way I indicated before: no function is allowed to store a delegate (assign it to a member variable / static variable, local variables could be allowed) unless it declares so as a flag to the delegate parameter. So class X { void delegate(int) memberDelegate; void f( void delegate( int ) ptr ) { memberDelegate = ptr; // <= Compiler error: "Cannot store a reference to a delegate parameter that is not declared 'global'" } } if the programmer needs f to store the delegate anyway, he would have then to e.g. change it to: "void f( void delegate( int ) global ptr )" Passing a nested function as delegate to such a function would then generate another compiler error: "Cannot pass local delegate as global delegate parameter" or something
Mar 01 2003
Sean L. Palmer wrote:What information would the compiler need to have in order to be able to detect such usage 100%?It would need to allocate an object at the start of the function that contains the frame pointer - this is what's now passed as the nested function's parent frame pointer. When the function exits, it sets the pointer's contents to null; nested functions that are called check that the pointer is currently non-null before continuing. Then it unpacks the value and continues. The compiler will have to check that a delegate does use the parent frame pointer before making this check.
Mar 01 2003
In article <b3ko0o$ueh$1 digitaldaemon.com>, Walter says...Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class: void startLogging(string logFilename) { class Foo { file_handle logfile; void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } } Foo f = new Foo; f.logfile = openFile(logFilename, "a"); registerInterruptHandler(12, f.handleInterrupt); } It's a little more work, but not so bad.Ah.. clever approach, certainly. Do-it-yourself lexical closures, similar to the Java approach. I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals. Also, in the above example, won't f get garbage collected soon? Does the collector need to track references to f's members for this construct to work? (Does it anyway? I'm not familiar with the techniques it uses...) Dan
Feb 27 2003
In article <b3lri6$1m8f$1 digitaldaemon.com>, Dan Liebgold says...In article <b3ko0o$ueh$1 digitaldaemon.com>, Walter says...No the pointer will be held in a delegate which is a object/function pointer pair. Since there is an active pointer to the object it won't be collected.Yup, you're right, that will fail in D. The way to make it work is to make handleInterrupt a member of a class: void startLogging(string logFilename) { class Foo { file_handle logfile; void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } } Foo f = new Foo; f.logfile = openFile(logFilename, "a"); registerInterruptHandler(12, f.handleInterrupt); } It's a little more work, but not so bad.Ah.. clever approach, certainly. Do-it-yourself lexical closures, similar to the Java approach. I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals. Also, in the above example, won't f get garbage collected soon? Does the collector need to track references to f's members for this construct to work? (Does it anyway? I'm not familiar with the techniques it uses...) Dan
Feb 27 2003
"Dan Liebgold" <Dan_member pathlink.com> wrote in message news:b3lri6$1m8f$1 digitaldaemon.com...I'm afraid I agree with Jeroen on this point though, it is very easy to introduce that subtle bug of referencing de-scoped locals.I'm thinking it may be possible to add a runtime check for that.Also, in the above example, won't f get garbage collected soon?No.Does the collector need to track references to f's members for this construct to work? (Does it anyway? I'mnotfamiliar with the techniques it uses...)It scans the entire stack for references. f.handleInterrupt will contain a reference to f, and the usual places that the delegate will be stored will be scanned.
Feb 27 2003
I'm thinking it may be possible to add a runtime check for that.It may even be possible to do static checking: the 'registerInterruptHandler(12, handleInterrupt)' in the example would have to be declared as registerInterruptHandler( int, global delegate()) to be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible You could discuss about the wording (local vs global or something else) or perhaps define a default ( all 'delegate' parameter values are assumed local (not storable) unless explicitly declared "global" (or "storable"), or vice versa )
Feb 27 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3mab5$1vde$1 digitaldaemon.com...toI'm thinking it may be possible to add a runtime check for that.It may even be possible to do static checking: the 'registerInterruptHandler(12, handleInterrupt)' in the example would have to be declared as registerInterruptHandler( int, global delegate())be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible You could discuss about the wording (local vs global or something else) or perhaps define a default ( all 'delegate' parameter values are assumedlocal(not storable) unless explicitly declared "global" (or "storable"), orviceversa )Some good thoughts there.
Feb 27 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3mab5$1vde$1 digitaldaemon.com...I think it can be done even without the "global" keyword, by analyzing the program and anotating every delegate argument of every fucntion with the golobal flag. This way you can get a initial set of functions and a set of rules. To get a result you would find the transitive closure. This is something like forward inference of expert sytems. I believe that you can solve this also using "backward inference" (like prolog uses) to resolve only one specific case. On the other side having the flag explicit can be good hint for a programmer.I'm thinking it may be possible to add a runtime check for that.It may even be possible to do static checking: the 'registerInterruptHandler(12, handleInterrupt)' in the example would have to be declared as registerInterruptHandler( int, global delegate()) to be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible You could discuss about the wording (local vs global or something else) or perhaps define a default ( all 'delegate' parameter values are assumed local (not storable) unless explicitly declared "global" (or "storable"), or vice versa )
Mar 01 2003
"Jeroen van Bemmel" <anonymous somewhere.com> writes:Okay, let's discuss. First, let's make the vocabulary clear. There are three kinds of callable entities: "function" - just the normal function. Contains the address of the function. "local delegate" - created by nested function or function literal Contains the address of the function and a pointer to the activation record of the function it is defined in. That activation record will be demolished (if it's allocated from the stack which is what we usually do) at some point. "global delegate" - created from a class member function Contains the address of the function and a pointer to its environment, which is an object allocated on the heap. The environment won't be destroyed until the delegate itself disappears. [I intentionally avoid the term "closure" because of my superstitiously traditional definition of closure: that it captures its environment. Global delegates are kind-of closures (although there environment == object), and local delegates are pseudo-closures which act like "real" closures until the environment goes out of scope, after which they cause terror and destruction.] Now the situation is that local delegates cannot be safely stored, while global delegates can. This is the fundamental difference. However, at the moment, there is only a single "delegate" concept in D. The user of a delegate doesn't know if it can store it or not. If this situation stays, it's likely that "Don't store local delegates" will be the first item in the book "D traps and pitfalls". Which would be scaringly close to those "Remember to write a virtual destructor" hints that C++ books are full of. The best situation would be if there would be no need for such advice. Delegates can be made safe in different ways, some of which are: 1. Relying on global error checking After compilation, check every store of every delegate and ensure that they never originate from a nested function or function literal. Pros: - doesn't need changes in the language spec - will probably prevent majority of problems Cons: - the source must be available (or just the information about where delegates are stored) - complexity - the resulting error message might still be intimidating to someone who's about to shoot himself in the foot: "Illegal local delegate store in function void foobar(delegate int() x) in flabbergast.d:234, called in function void xyz() in application.d:95". Still, it would be better than letting the programmer actually shoot himself in the foot. 2. Relying on lint-like tools The same as above, except implemented as a separate tool. Pros: - doesn't complicate the compiler nor the language Cons: - makes the programmer's life more complex, though - and it goes without mention that the language would instantly have a C-like kludgy feeling 3. Making the two types of delegates different types. Like this: (assume that "global delegate" is made the default. This can be questioned, but at least that decision won't break the existing code) - objects of type "delegate" can be stored - objects of type "local delegate" can be used and passed around, but not stored - there should be an implicit conversion "delegate -> local delegate" - optionally, the programmer should be able to convert local delegates to "real" delegates, if he's 100% certain that the local delegate won't outlive its activation record. The cast should be as ugly and eye-catching as possible, by similar reasoning as with C++'s reinterpret_cast<>. For instance, it could be useful for storing a temporary copy of the delegate (for some reason). Pros: - safety: you cannot store a local delegate unknowingly, which catches the vast majority of possible delegate bugs - speed (the performance is the same as it is now) - simplicity (from the implementor's viewpoint) - just add a new type, a conversion, and there you go Cons: - complexity in the interface: you'll have to state "local delegate(...)" everywhere where you know that you won't be storing the delegate. This is precisely the same problem as with C interfaces and const: if you have, for example, the function void print(char *c);, you can't give it a string literal, because that's a const char *. 4. Getting rid of local delegates altogether by allocating some of the allocation records on the heap This one would be something pretty cool if only possible. The idea is to allocate from the heap those allocation records that have escaping delegates. It's just a hot-headed idea and I'm not sure if it's at all feasible. Usually actication records are either all allocated on the stack or on the heap. I found one description of a hybrid solution at http://www.cs.indiana.edu/~dyb/papers/stack-abstract.html, though. (Mainly targeted for languages with first-class continuations.) Pros: - safety: since all delegates are true closures, no need to worry about pointers to long-gone activation records. - simplicity: the programmer need not worry about local or global delegates - he'll only know about delegates. Cons: - Speed. Calling functions with escaping delegates is slower because of heap-allocation and potential cache misses due to lost memory locality. However, if the compiler could do global analysis and notice that the delegate won't be stored anywhere, it could put the allocation record in the stack as usually and refer to that. - Complexity in the implementation side, especially if the above optimization is required 5. Combining the approaches 3 and 4 Include both local delegates and delegates as distinct types. Whenever local delegate is converted into a global delegate is required, the activation record of the originating function of the delegate is marked to be heap-allocated. This ensures that there can be no dangling references to the activation record. Pros: - speed: when a local delegate is required, we can use one without allocating anything on the heap - flexibility: when a global delegate is required, we can still use a local one with the cost that the activation record is allocated from the heap Cons: - complexity of both the interface (need two different types) and the implementation (must be able to heap-allocate the activation records) In summary: all of solutions (3)-(5) are safe. Solution (3) is straightforward and emphasizes ease of implementation. Solution (4) emphasizes ease of use. Solution (5) provides both speed and flexibility with the cost of added complexity both on the usage and implementation side. In addition, there probably are feasible solutions that my imagination isn't capable of coming up with. Oh yeah, and then we have yet another alternative: 6. Letting things be as they are now - possibly unsafe, but simple, efficient and mostly useful. Delegates are definitely good, and experienced developers will know to be careful when they encounter a delegate. If they're even a bit safety-conscious, they'll never store the delegate, because it might contain a pointer to the stack frame. Especially in a context where you can never know where the delegate comes from, and you have no way to inform that you must be able to store the delegate. However, when programming projects get larger, someone will, eventually, in a moment of dim-wittedness, hurry, carelessness, or fatigue, store a pointer to a local delegate and then someone other will happily call it in an unexpected situation (say, an error handler which is called very rarely and tested only a couple of specific situations, or perhaps even untested.) And there we have a catastrophe. (To be dramatic, I'll now have to include some examples of software catastrophes to gain appreciation to my view :-) http://courses.cs.vt.edu/~cs3604/lib/Therac_25/Therac_1.html http://www.ima.umn.edu/~arnold/disasters/ariane.html http://www.cs.tau.ac.il/~nachumd/verify/horror.html I'd live happily with alternatives 3, 4 or 5 - and even 6, but I'd be much more comfortable with some kind of guarantee that a nonexistent stack frame will never be accessed. -AnttiI'm thinking it may be possible to add a runtime check for that.It may even be possible to do static checking: the 'registerInterruptHandler(12, handleInterrupt)' in the example would have to be declared as registerInterruptHandler( int, global delegate()) to be allowed to store the reference to the delegate somewhere. Passing 'handleInterrupt' would then be disallowed, since nested functions are of type "local delegate" and thus not assignment compatible You could discuss about the wording (local vs global or something else) or perhaps define a default ( all 'delegate' parameter values are assumed local (not storable) unless explicitly declared "global" (or "storable"), or vice versa )
Mar 01 2003
Antti Sykari <jsykari gamma.hut.fi> wrote in news:87n0kebvdg.fsf_-_ hoastest1-8c.hoasnet.inet.fi:The best situation would be if there would be no need for such advice. Delegates can be made safe in different ways, some of which are:7. Marking functions that will hold a reference. void delagate() foo; // stored flag marks fn as a as being // stored by the function void setFn(stored void deligate() a) { foo = a; } void one() { // Global scope } void bar() { int i; void two() { // Does not access local scope } void three() { // Accesses local scope i = 5; } setFn(&one); // Ok setFn(&two); // Ok setFn(&three); // Error } Could be used for auto objects too void foo(stored Object a) { } void bar() { auto Object o = new Object(); foo(o); // Error }
Mar 01 2003
A funny thought crossed my mind... Are structs now equivalent to classes in the sense that struct member functions can be stored into delegates? (They would, usually, be local delegates in the sense that structs tend to be stack-allocated.) Should they? -Antti
Mar 01 2003
In article <b3ko0o$ueh$1 digitaldaemon.com>, Walter says...void startLogging(string logFilename) { class Foo { file_handle logfile; void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } } Foo f = new Foo; f.logfile = openFile(logFilename, "a"); registerInterruptHandler(12, f.handleInterrupt); } It's a little more work, but not so bad.Here's an idea for some syntactic sugar to make that pill easier to swallow: In C++, classes can implement cast methods, for example: class IntObj { int data; operator float { return (float)data; } } How about an overloadable operator such that you can cast a class instance to a delegate? The declaration syntax may be difficult to get right. However, with it you could do this (the example assumes you add the keyword 'closure' to denote the technique): void startLogging(string logFilename) { class Foo { file_handle logfile; this (file_handle log) { logfile = log; } void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } delegate void (int) closure { return handleInterrupt; } } Foo f = new Foo(openFile(logFilename, "a")); registerInterruptHandler(12, handleInterrupt); } So in this example registerInterruptHandler received a normal "delegate void (int)", just like it expected. "handleInterrupt" would be restricted by the compiler to only referencing globals or symbols within the Foo class (it would have no reference to anything in the startLogging function). Anything the delegate needs, pass it to Foo's constructor. One step further: void startLogging(string logFilename) { class Foo { file_handle logfile; this (char[] name) { logfile = openFile(name, "a") } void handleInterrupt(int interrupt) { writeMessage(interrupt, logfile); } delegate void (int) closure { return handleInterrupt; } } registerInterruptHandler(12, new Foo(logFilename)); } Voila... lexical closures, without the implementational complexity, and without any lurking stack traps for the unwary. Dan
Feb 28 2003
Here's an idea for some syntactic sugar to make that pill easier toswallow:In C++, classes can implement cast methods, for example: class IntObj { int data; operator float { return (float)data; } } How about an overloadable operator such that you can cast a class instanceto adelegate?Talking about hidden and obscure implicit behavior! (You should interpret the above as "no, I don't think that's a good idea") Why not? a) "f.handleInterrupt" is a lot shorter to type than declaring & implementing a 'delegator cast operator' b) since it's a local function/class, it's sure to have limited size and functionality. In fact, it should be as short as possible to keep readability acceptable c) overloadable operators are a questionable syntactic-sugar feature of C++. Overloadable type-cast operators are a nightmare, since the implicit type casts they cause go against all type safety principles d) "casting a class instance to a delegate" - just reading that should raise questions about the semantics...
Mar 01 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3qvu1$2a5o$1 digitaldaemon.com...a) "f.handleInterrupt" is a lot shorter to type than declaring & implementing a 'delegator cast operator'I do agree.b) since it's a local function/class, it's sure to have limited size and functionality. In fact, it should be as short as possible to keep readability acceptableWhen you need a lexical closures, you need to create an object for it currently. This adds lines for the object plus a line for the explicit delegate creation.c) overloadable operators are a questionable syntactic-sugar feature of C++. Overloadable type-cast operators are a nightmare, since the implicit type casts they cause go against all type safety principlesI do agree.d) "casting a class instance to a delegate" - just reading that should raise questions about the semantics...I'm not sure about this one. You are probably right, but on the other side it looks very similar to C++ functors. That is not so bad. Still it can be cast to more delegates. Yes, it may be better to create the delegate explicitly (even when it is more to type). Still, there is diference when compared with point c. It canot cause some chain of implicit casts (delegate cannot be casted to anything else). So may be worth a try. What do other think?
Mar 01 2003
You're right... and you can't mark a user-defined cast as "explicit". I'd much rather have just constructors. Problem is, you can't add constructors to other classes such as "int". Seanc) overloadable operators are a questionable syntactic-sugar feature ofC++.Overloadable type-cast operators are a nightmare, since the implicit type casts they cause go against all type safety principles
Mar 01 2003
"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message news:b3sc9l$2uvl$1 digitaldaemon.com...You're right... and you can't mark a user-defined cast as "explicit". I'd much rather have just constructors. Problem is, you can't addconstructorsto other classes such as "int".To me, a need for "explicit" is just a sign that there is something inherently wrong with the 'feature' of implicit object creation when a single-argument constructor exists. Consider a function void f( SomeClass c ) That means, that c needs to be of type 'SomeClass' (or a subclass of that). C++ actually allows you to have another class with a 1-argument constructor class CompletelyDifferentClass { CompletelyDifferentClass( SomeClass c ) // 1-argument constructor } and then (perhaps even depending on visiblity, I'm not sure here, and also if SomeClass is not abstract) you can do CompletelyDifferentClass d; f( d ) Reading this code without the class or function declarations would make you think that either (1) f takes a 'CompletelyDifferentClass' as an argument, or (2) a superclass thereof. Instead, _implicitly_ a new, temporary object of class SomeClass is created, using the 1-argument constructor. Horror! No reference to 'SomeClass' is made in the above code fragment, yet it is involved. So this means that a 1-argument constructor models "is_a" (while in fact it is "can_be_constructed_from_a") I vote for explicit object construction always, preferrably with always the same language construct ("new" will do fine). appeal to me either. They claim that "everything is an object" (Marketing: "much better than Java"), but underwater some things are more object than others, heavily affecting performance as a "side effect"
Mar 02 2003
"Jeroen van Bemmel" <anonymous somewhere.com> writes:"Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message news:b3sc9l$2uvl$1 digitaldaemon.com...You probably meant vice versa: class SomeClass { SomeClass( CompletelyDifferentClass c ); }You're right... and you can't mark a user-defined cast as "explicit". I'd much rather have just constructors. Problem is, you can't addconstructorsto other classes such as "int".To me, a need for "explicit" is just a sign that there is something inherently wrong with the 'feature' of implicit object creation when a single-argument constructor exists. Consider a function void f( SomeClass c ) That means, that c needs to be of type 'SomeClass' (or a subclass of that). C++ actually allows you to have another class with a 1-argument constructor class CompletelyDifferentClass { CompletelyDifferentClass( SomeClass c ) // 1-argument constructor }and then (perhaps even depending on visiblity, I'm not sure here, and also if SomeClass is not abstract) you can do CompletelyDifferentClass d; f( d )Could be useful, if, for example, SomeClass is replaced by "int" and CompletelyDifferentClass with "float". Sprinkling the code with explicit conversion function calls may make it less readable and make it more difficult to spot the "really dangerous" explicit conversions from the code. -Antti
Mar 02 2003
Perhaps they should have chosen to make explicit behavior the default, and added an "implicit" keyword instead. Implicit conversion is still very useful under controlled circumstances. I find that, like operator overloading and templates, implicit construction/conversion is one of those language features that people strongly lobby against because they have personally been burned by it at some point in the past. What they don't realize is that, with proper care, it can be a VERY powerful language feature that make solving some particular problem feasible or at least easier. When combined with templates, implicit conversion might allow you to express something that you couldn't otherwise say because you don't know or don't have handy access to the name of the type you want it converted to, or don't care if the types match exactly. I can't justify exclusion of a feature because you don't happen to like it and have a description of a circumstance in which you shot yourself in the foot with it. In C++ I use implicit user-defined conversion to bool, int, and float all the time. A colleague at work just made a "smart enum" class that uses it; this enum knows how to convert itself to and from a string automatically. Sean "Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3t8hp$blq$1 digitaldaemon.com..."Sean L. Palmer" <seanpalmer directvinternet.com> wrote in message news:b3sc9l$2uvl$1 digitaldaemon.com...I'dYou're right... and you can't mark a user-defined cast as "explicit".that).much rather have just constructors. Problem is, you can't addconstructorsto other classes such as "int".To me, a need for "explicit" is just a sign that there is something inherently wrong with the 'feature' of implicit object creation when a single-argument constructor exists. Consider a function void f( SomeClass c ) That means, that c needs to be of type 'SomeClass' (or a subclass ofC++ actually allows you to have another class with a 1-argumentconstructorclass CompletelyDifferentClass { CompletelyDifferentClass( SomeClass c ) // 1-argument constructor } and then (perhaps even depending on visiblity, I'm not sure here, and also if SomeClass is not abstract) you can do CompletelyDifferentClass d; f( d ) Reading this code without the class or function declarations would makeyouthink that either (1) f takes a 'CompletelyDifferentClass' as an argument, or (2) a superclass thereof. Instead, _implicitly_ a new, temporary object of class SomeClass is created, using the 1-argument constructor. Horror!Noreference to 'SomeClass' is made in the above code fragment, yet it is involved. So this means that a 1-argument constructor models "is_a" (while in fact it is "can_be_constructed_from_a") I vote for explicit object construction always, preferrably with alwaysthesame language construct ("new" will do fine). appeal to me either. They claim that "everything is an object" (Marketing: "much better than Java"), but underwater some things are more object than others, heavily affecting performance as a "side effect"
Mar 02 2003
"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3kh99$qoj$1 digitaldaemon.com...BTW, now that I'm thinking about this: One of the worse features of JavaIMOis the rediculous amount of resources (memory) it uses for simple things. The garbage collector is good, but it allocates so many small objects (the example I gave allocates one for the function object).True, one thing I was going to point out about your example is that the D way of doing it is far more efficient of run time and memory.The advantage is though that I can return this object from my function,anduse it elsewhere. What happens if I return a reference to a nestedfunctiondelegate in D? Is it still valid, is there also some memory allocated underneath? You would expect it to go out of scope, so the compiler should issue a warning "returning reference to local function" or somethingNo memory is allocated underneath, a hidden pointer to the stack frame is just saved and passed as a parameter to f(). That means, if the function goes out of scope, it is just as much an error as returning a pointer to a local: int *wrong() { int i; return &i; }
Feb 27 2003
Walter wrote:"Jeroen van Bemmel" <anonymous somewhere.com> wrote in message news:b3grne$1hat$1 digitaldaemon.com...Why does function nesting impact automatic in-lining? Is a nested function more likely to be in-lined? BillOK, I'm gonna play the devil's advocate some more now, hope you'll excusemefor that, but...why is it useful to be able to declare nested functions?Speak of the devil <g> I just wrote this example: www.digitalmars.com/d/pretod.html#codefactoring The C version is done with macros, but if you want, try it with C++ inline functions.
Feb 26 2003
"Bill Cox" <bill viasic.com> wrote in message news:3E5CEABE.30501 viasic.com...Walter wrote:inlineSpeak of the devil <g> I just wrote this example: www.digitalmars.com/d/pretod.html#codefactoring The C version is done with macros, but if you want, try it with C++A D nested function is equally likely to be inlined as a non-nested one.functions.Why does function nesting impact automatic in-lining? Is a nested function more likely to be in-lined?
Feb 26 2003
The linker doesn't seem to like it when you put two function literals in the same function. Also, this doesn't seem to work. Should it? (not that it's useful in any way) delegate() { printf("stop the insanity!\n"); }(); Walter wrote:Some long overdue features. www.digitalmars.com/d/changelog.html
Feb 25 2003
"Andy Friesen" <andy ikagames.com> wrote in message news:b3h201$1l7n$1 digitaldaemon.com...The linker doesn't seem to like it when you put two function literals in the same function.I should fix that.Also, this doesn't seem to work. Should it? (not that it's useful in any way) delegate() { printf("stop the insanity!\n"); }();It should work, although I agree it's useless.
Feb 25 2003
"Walter" <walter digitalmars.com> wrote in news:b3fdlo$kp1$1 digitaldaemon.com:Some long overdue features. www.digitalmars.com/d/changelog.htmle:\Programming\tools\dmd\bin\..\..\dm\bin\link.exe err,,,user32 +kernel32/noi; OPTLINK (R) for Win32 Release 7.50B1 Copyright (C) Digital Mars 1989 - 2001 All Rights Reserved err.obj(err) Error 42: Symbol Undefined __init_TypeInfo_Ag --- errorlevel 1 int main(char[][] argv) { int[byte[]] foo; static byte[] bar = [ 65, 66, 67 ]; foo[bar] = 1; return 0; }
Feb 25 2003
"Walter" <walter digitalmars.com> wrote in news:b3fdlo$kp1$1 digitaldaemon.com: Walter, here are three issues I encountered with DMD 0.56 and 0.57: The template argument deduction seems to be broken. The Object type is ignored by the compiler. class A { } class B : A { } template TFoo(T : A) { } instance TFoo(B); Causes this error message: instance TFoo(B ) does not match any template declaration -- DMD.zip contains the compiler twice: See file "DMD\.EXE". -- I compiled some D code that compiles and runs fine, if the option -O is not used. If option -O is turned on, the compiler says: Internal error: ..\ztc\cgcod.c 2198 Farmer
Feb 26 2003
"Farmer" <itsFarmer. freenet.de> wrote in message news:Xns932EE4CF28753itsFarmer 63.105.9.61...I compiled some D code that compiles and runs fine, if the option -O isnotused. If option -O is turned on, the compiler says: Internal error: ..\ztc\cgcod.c 2198I need an example. Thanks! -Walter
Feb 26 2003
"Farmer" <itsFarmer. freenet.de> wrote in message news:Xns932EE4CF28753itsFarmer 63.105.9.61...DMD.zip contains the compiler twice: See file "DMD\.EXE".Fixed.
Feb 26 2003
Function pointers don't print diagnostics using the new syntax, such as with: void main () { void delegate () foo; static void bar () { } foo = bar; } Prints "cannot implicitly convert void(*)() to void delegate()".
Feb 27 2003
This one is longstanding. f.d: import g; void main () { printf ("%d\n", foo); // "foo" shouldn't be imported! } g.d: static int foo; So "static" is being ignored. Now would be a good time to move to "private" to mean the same thing instead, and to make applying "static" to a symbol at the module level an error. I think it would be a good idea, anyway. I would also like the change where "protected" means that the symbol isn't imported, but it IS accessible by using the module name.
Feb 27 2003
Burton Radons <loth users.sourceforge.net> wrote in news:b3ml3m$25bi$1 digitaldaemon.com:I would also like the change where "protected" means that the symbol isn't imported, but it IS accessible by using the module name.I'll vote for this too.
Feb 27 2003
"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3ml3m$25bi$1 digitaldaemon.com...This one is longstanding. f.d: import g; void main () { printf ("%d\n", foo); // "foo" shouldn't be imported! } g.d: static int foo; So "static" is being ignored. Now would be a good time to move to "private" to mean the same thing instead,It already does.and to make applying "static" to a symbol at the module level an error.At the moment, it's a no-op.
Feb 27 2003
Walter wrote:"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3ml3m$25bi$1 digitaldaemon.com...Ack, there're places in Phobos which assume that static has the C meaning then: crc32.d (crc32_table), regexp.d (isword, inf, replace3), and stream.d (iswhite, isdigit, isoctdigit, ishexdigit).So "static" is being ignored. Now would be a good time to move to "private" to mean the same thing instead,It already does.
Feb 27 2003
"Walter" <walter digitalmars.com> wrote in message news:b3mnhp$26ii$1 digitaldaemon.com...Will this no-op change to "done"? I do not like too much when one has more options to do the same. There are already two ways how to define arrays: I'm also confused with the fucntion literal declaration. Grammar states: FunctionLiteral ::= function Type ( ParameterDeclarations ) but the example just below goes like: int function(char c) fp; instead of: function int(char c) fp; Is this intentional ambiguity? Or does it have some meaning unknown to me? I'm also playing with the idea that the return type of a function should be always explicitly stated - to enforce this by grammar. I have two reasons: a) looks beter b) your default is void (contrary to int in C/C++) These ambiguities at such a low level have some *bad* conssequences: a) one need to learne them, that there are more ways to achieve exaclty the same and less readable code thereof b) tool implementations will be more comlicated because of this (and reason? good feel only ... because it doe not add any new functionality) c) it makes seraching in the source code more complicated (transition to more complicated regular expressions) I do not know how other guys think about this, may be I'm alone who minds it :)So "static" is being ignored. Now would be a good time to move to "private" to mean the same thing instead,It already does.and to make applying "static" to a symbol at the module level an error.At the moment, it's a no-op.
Mar 01 2003
"Peter Hercek" <vvp no.post.spam.sk> wrote in message news:b3rerf$2hdt$1 digitaldaemon.com..."Walter" <walter digitalmars.com> wrote in messagenews:b3mnhp$26ii$1 digitaldaemon.com...?. no-op means "it's ignored".Will this no-op change to "done"?So "static" is being ignored. Now would be a good time to move to "private" to mean the same thing instead,It already does.and to make applying "static" to a symbol at the module level an error.At the moment, it's a no-op.I'm also confused with the fucntion literal declaration. Grammar states: FunctionLiteral ::= function Type ( ParameterDeclarations ) but the example just below goes like: int function(char c) fp;That is not a function literal, but a declaration of a pointer to a function.instead of: function int(char c) fp;That is invalid.I'm also playing with the idea that the return type of a function should be always explicitly stated - to enforce this by grammar. I have two reasons: a) looks beter b) your default is void (contrary to int in C/C++)The default isn't int in C/C++ anymore, it must be specified.These ambiguities at such a low level have some *bad* conssequences: a) one need to learne them, that there are more ways to achieve exaclty the same and less readable code thereof b) tool implementations will be more comlicated because of this (and reason? good feel only ... because it doe not add any new functionality) c) it makes seraching in the source code more complicated (transition to more complicated regular expressions) I do not know how other guys think about this, may be I'm alone who minds it :)I did it that way because the function literal syntax was already rather longish.
Mar 01 2003
"Walter" <walter digitalmars.com> wrote in message news:b3rhmu$2iju$1 digitaldaemon.com...Got it! Thanks.I'm also confused with the fucntion literal declaration. Grammar states: FunctionLiteral ::= function Type ( ParameterDeclarations ) but the example just below goes like: int function(char c) fp;That is not a function literal, but a declaration of a pointer to a function.instead of: function int(char c) fp;That is invalid. I did it that way because the function literal syntax was already rather longish.
Mar 01 2003
Weird bug with static: struct foo { static struct bar { int x; } } void main () { printf ("%d\n", foo.bar.size); } This prints 1, rather than 4!
Feb 27 2003
"Walter" <walter digitalmars.com> wrote in news:b3fdlo$kp1$1 digitaldaemon.com:Some long overdue features. www.digitalmars.com/d/changelog.htmlThe following program prints: 12,14 1244952,14 What's with the value of i in the nested fuction? void foo(void delegate() baz) { baz(); } void bar(int i) { int j = 14; printf("%d,%d\n",i,j); void fred() { printf("%d,%d\n",i,j); } foo(&fred); } int main(char[][] argv) { bar(12); return 0; }
Feb 27 2003
Patrick Down <pat codemoon.com> wrote in news:Xns932FF17C3FB85patcodemooncom 63.105.9.61:"Walter" <walter digitalmars.com> wrote in news:b3fdlo$kp1$1 digitaldaemon.com:Similar example. i and j are messed up in this one. void frelled(void delegate() baz) { baz(); } class Foo { void bar(int i) { int j = 14; printf("%d,%d\n",i,j); void fred() { printf("%d,%d\n",i,j); } frelled(&fred); } } int main(char[][] argv) { Foo f = new Foo(); f.bar(12); return 0; }Some long overdue features. www.digitalmars.com/d/changelog.htmlThe following program prints: 12,14 1244952,14 What's with the value of i in the nested fuction? void foo(void delegate() baz) { baz(); } void bar(int i) { int j = 14; printf("%d,%d\n",i,j); void fred() { printf("%d,%d\n",i,j); } foo(&fred); } int main(char[][] argv) { bar(12); return 0; }
Feb 27 2003
They're both compiler bugs and I'll fix them. -Walter
Feb 28 2003
Now with nested and inline functions: int foo () { return 0x12345678; } void main () { int nestedFunction () { return foo (); } printf ("%08X\n", foo ()); printf ("%08X\n", nestedFunction ()); printf ("%08X\n", delegate int () { return foo (); }); } This prints: 12345678 12345678 0012FF1C Replacing "delegate" with "function" prints "0040305C".
Feb 27 2003
Burton Radons wrote:printf ("%08X\n", delegate int () { return foo (); });Gah, I'm a stupidhead. Sorry.
Feb 27 2003
This requires multiple modules: f.d: module foo.bar; import g; g.d: import f; Using "dmd f.d" complains that "g.d(1): module f is in multiple packages foo.bar". Secondly, you say that you made "private" mean "static", but that doesn't work either: f.d: import g; void main() { int x = foo; } g.d: private int foo; Using "dmd -c f.d" on this code compiles, even though it's an error.
Mar 02 2003
"Burton Radons" <loth users.sourceforge.net> wrote in message news:b3skqh$2hb$1 digitaldaemon.com...This requires multiple modules: f.d: module foo.bar; import g; g.d: import f; Using "dmd f.d" complains that "g.d(1): module f is in multiple packages foo.bar".Hmmm. Looks like a compiler bug.Secondly, you say that you made "private" mean "static", but that doesn't work either: f.d: import g; void main() { int x = foo; } g.d: private int foo; Using "dmd -c f.d" on this code compiles, even though it's an error.I just fixed that one last night <g>.
Mar 02 2003
Here's a symbol bug: class A { class Child { } } class B { class Child { static int value; } } void main() { printf ("%d\n", B.Child.value); } Compiling this with "dmd module.d", I get "module.d(18): no property 'value' for type 'Child'". A and B have nested classes with the same name, but B.Child resolves to A.Child.
Mar 02 2003