digitalmars.D - Lazy eval
- Frank Benoit (23/23) Aug 21 2006 I think the lazy eval is a great feature, but in this form it has also
- Frank Benoit (6/6) Aug 21 2006 One more argument against the lazy-eval in the current form:
- Pragma (22/30) Aug 21 2006 You're right that the lazy eval, in it's fullest form, is a very
- Walter Bright (15/44) Aug 21 2006 It's true there is no clue from the user's side which it is. But there
- Frank Benoit (8/12) Aug 21 2006 I use the { return ...; } very much. And I like it very much. And I
- kris (30/45) Aug 21 2006 I think the new sugar works really well where the code in question is
- Tom S (6/59) Aug 21 2006 You have a typo in the 3rd sample, it should read:
- Sean Kelly (4/64) Aug 21 2006 I'm not sure I'd want to do away with option 3 unless option 2 supported...
- kris (9/86) Aug 21 2006 True; and some have pointed out potential problems with #2.
- Frank Benoit (5/10) Aug 21 2006 Writing it without {} will always be obfuscating, if the reader doesn't
- Derek Parnell (9/27) Aug 21 2006 Would it possible to use ...
- kris (2/35) Aug 21 2006 arghhh!!! Please ... cast() is only for exceptional circumstances :(
- nobody (3/43) Aug 21 2006 cast( :-) )
- Derek Parnell (10/43) Aug 21 2006 And this situation is not exceptional? Okay, than how about a keyword .....
- Lutger (8/36) Aug 21 2006 As for ambiguity between overloads, doesn't it make more sense to change...
- Oskar Linde (17/39) Aug 22 2006 A feature request that pops up now and then is to at least allow using
- Tom S (49/55) Aug 21 2006 While I partly agree with you, Frank, I don't want to dismiss this new
- Frank Benoit (17/19) Aug 21 2006 If an argument is in/out/inout it is clear from the content and context.
- Tom S (8/23) Aug 21 2006 Nah, just that the arg might be left not-evaluated... I wouldn't use it
- Frank Benoit (6/8) Aug 21 2006 Yes, and that is my only point.
- Walter Bright (9/23) Aug 21 2006 While I understand your concern, I don't think the examples illustrate
- kris (4/31) Aug 21 2006 Can you at least make it optional? Or, can you come up with an operator
- Frank Benoit (23/25) Aug 21 2006 I am a programmer. I like them. I like to understand my and foreign
- Derek Parnell (8/33) Aug 21 2006 Huh? You asked them all? You didn't ask me and I like it.
- Walter Bright (10/15) Aug 21 2006 Did you use it before 0.165? Did anyone? Everyone I'd show the { }
- Derek Parnell (35/53) Aug 21 2006 Yes. However I couldn't release it in Build until GDC caught up to the
- Walter Bright (40/91) Aug 21 2006 It's good to be wary of unusual new features. There are usually
- Derek Parnell (18/20) Aug 21 2006 On Mon, 21 Aug 2006 20:54:36 -0700, Walter Bright wrote:
- Walter Bright (3/19) Aug 22 2006 I agree.
- Sean Kelly (27/62) Aug 22 2006 In my limited experience, the attendees of those conferences are what
- BCS (10/16) Aug 22 2006 The problem isn't that changing things, breaks stuff. It it that going
- Walter Bright (10/17) Aug 22 2006 Changing the API is always problematic, including with traditional
- John Reimer (15/23) Aug 21 2006 Zzzz? I really don't think that was the response. I believe most peopl...
- kris (11/33) Aug 21 2006 I did, and continue to do so, along with a number of others I know
- Walter Bright (5/9) Aug 21 2006 I wasn't thinking of callbacks. Take a look at the common practice:
- Chris Miller (33/41) Aug 21 2006 I very much like the { } syntax, it was just surprising to me.
- Walter Bright (13/52) Aug 21 2006 A cast of a value to:
- Sai (23/23) Aug 22 2006 Other people already proposed the { expr } syntax, I liked it too, can't...
- kris (2/35) Aug 22 2006 Sure is better than deliberately introducing ambiguity into the language...
- kris (22/54) Aug 21 2006 So, we have some existing code for logging. Very efficient, and highly
- Walter Bright (13/40) Aug 21 2006 Yes.
- kris (14/66) Aug 21 2006 lol! I can just imagine 1001 log statements inlined within the log code ...
- Walter Bright (25/57) Aug 21 2006 I've been thinking a lot about the escape problem. I'm pretty sure that:
- kris (22/94) Aug 22 2006 Asserting "programmers won't type the {}" as the reason for deliberate
- kris (15/19) Aug 22 2006 And what about those cases where the expression is somewhat more
- Walter Bright (15/38) Aug 22 2006 It's straightforward at one level - if a delegate does not reference any...
- Sean Kelly (19/31) Aug 22 2006 I've been working on a predicate-oriented algorithm module on and off
- Walter Bright (21/34) Aug 22 2006 Good - I don't think any of us are prescient enough to see all the
- Sean Kelly (25/56) Aug 22 2006 C++ templates are well-intentioned and really quite powerful, but
- kris (9/83) Aug 22 2006 Tom's suggestion is to introduce a different style of delegate for this
- Oskar Linde (57/72) Aug 25 2006 Interesting. I have been doing some of this myself too, but my available...
- Sean Kelly (14/85) Aug 31 2006 I agree. And given D's somewhat limited overloading mechanism, there
- Chris Nicholson-Sauls (5/110) Aug 31 2006 I still think a good move would be to allow delegates as foreach aggrega...
- kris (2/42) Aug 22 2006 Can't do that; the char[] version have to stay.
- Oskar Linde (10/55) Aug 22 2006 This may not be a very clean solution, but you could make them
- Walter Bright (2/3) Aug 22 2006 Why?
- kris (2/8) Aug 22 2006 Because my Boss says so
- Paolo Invernizzi (7/17) Aug 23 2006 kris, really, is it a joke?
- John Reimer (4/19) Aug 23 2006 I think he was being sarcastic.
- kris (75/107) Aug 23 2006 No sarcasm required. The recent development of deliberately introducing
- Paolo Invernizzi (8/15) Aug 24 2006 I've followed the discussion.
- kris (8/26) Aug 24 2006 That's simple ~ time a trivial function by calling it several million
- kris (14/20) Aug 22 2006 Further, I had to remove the very useful delegate() overload that was
- Sean Kelly (7/10) Aug 22 2006 Really? Are you saying that if the receiving function is short enough
- Walter Bright (5/15) Aug 22 2006 It doesn't do it now, but it is possible to do. What an advanced
- BCS (8/28) Aug 23 2006 a.k.a
- Unknown W. Brackets (13/48) Aug 21 2006 Well, if I saw:
- BCS (13/27) Aug 22 2006 I feel confident I can document all sorts of bazaar things in my own
- Frank Benoit (2/16) Aug 22 2006 Well said.
- Walter Bright (11/25) Aug 22 2006 I agree with you that comments are always wrong, out of date, or
- Stewart Gordon (7/28) Aug 23 2006 If that's the case, then something's wrong. If funcRetInt returns a
- Frank Benoit (7/17) Aug 23 2006 This is the first example, showing a ambiguity
- Stewart Gordon (22/39) Aug 23 2006 If one overload matches exactly, it goes without saying that that's the
- Chris Nicholson-Sauls (39/90) Aug 23 2006 That's what I would have thought as well, but its already been said that...
- Stewart Gordon (23/56) Aug 25 2006 Said by whom, where exactly? Moreover:
- Chris Nicholson-Sauls (7/49) Aug 25 2006 I know I've read it, don't recall exactly by whom or suchlike. However,...
I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once? There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt ); It would be really important for me to have readable code. I want to look at the code, and want to have an impression what will happen. What really would help, is if the the delegate is marked in some way as such. In the last releases of DMD the {} syntax was there. With it you needed the return statement. Perhaps we can choose the {} syntax with an optional return statement.... { "abc" } => char[] delegate() { return "abc"; } => char[] delegate() func( "abc" ) calls func( char[] a ) func({ "abc" }) calls func( char[] delegate() dg ) func({ return "abc"; }) calls func( char[] delegate() dg ) With that syntax one can immidiatly see "this is a delegate, if it is called or not or more than once depends on func()" and the more typing of {} is not too much. Frank
Aug 21 2006
One more argument against the lazy-eval in the current form: If you have no chance to figure out what the code does, without looking at docs or function signature.... you will end up in writing 3 times more lines. Making each statements separated, to be sure to get them evaluated exactly once, without looking all that stuff up.
Aug 21 2006
Frank Benoit wrote:One more argument against the lazy-eval in the current form: If you have no chance to figure out what the code does, without looking at docs or function signature.... you will end up in writing 3 times more lines. Making each statements separated, to be sure to get them evaluated exactly once, without looking all that stuff up.You're right that the lazy eval, in it's fullest form, is a very stealthy (and arguably confusing) modification to the language. I have some similar concerns myself. However, Walter makes use of a slightly more obvious idiom in his discussion of this feature: void log(char[] delegate() dg) { if (logging) fwritefln(logfile, dg()); } void foo(int i) { log( { return "Entering foo() with i set to " ~ toString(i); }); } IMO, this is probably the best way to go as it is perfectly obvious what is going on. I like to think of it's counterpart (completely implicit conversion of expressions to delegates) as something a little more apt for generic programming - like being able to swap out a delegate for something else entirely. -- - EricAnderton at yahoo
Aug 21 2006
Frank Benoit wrote:I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );That's right.It would be really important for me to have readable code. I want to look at the code, and want to have an impression what will happen.I hear you, but I'll argue that the onus is on the function designer to have a name for the function that clues the user in to what it does. I know my example code has functions named "foo" a lot, but such a generic meaningless name would be unacceptable for production code.What really would help, is if the the delegate is marked in some way as such. In the last releases of DMD the {} syntax was there. With it you needed the return statement. Perhaps we can choose the {} syntax with an optional return statement.... { "abc" } => char[] delegate() { return "abc"; } => char[] delegate() func( "abc" ) calls func( char[] a ) func({ "abc" }) calls func( char[] delegate() dg ) func({ return "abc"; }) calls func( char[] delegate() dg ) With that syntax one can immidiatly see "this is a delegate, if it is called or not or more than once depends on func()" and the more typing of {} is not too much.I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.
Aug 21 2006
I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.I use the { return ...; } very much. And I like it very much. And I fully agree that the return..; is annoying, because if often use it for a search criteria or something like that. The syntax { singlestatement } would be pretty cool. But leaving the braces completely out it terrible. And {} is really not too much to type. Imagine the terrible bugs that will occur with that. I think one of the biggest advantages of D vs. C++ is the readability. That is so important. Really. Please don't destroy that.
Aug 21 2006
Frank Benoit wrote:I think the new sugar works really well where the code in question is *designed* around the notion of callbacks. The cues are already solidly in place at that point, so it may be less of an issue there? I'm thinking specifically of the logging examples, and some fairly advanced distributed processing paradigms. However, in typical or 'mixed' D programming, the lack of cues in this latest sugar will surely become problematic. Walter suggests the author of the callee should use an appropriate name, such that overloading would (presumeably) not be an issue and the appropriate cues would be retained. One has to wonder if that is a good idea or not. If it were possible to make the '{' '}' optional, for example, then another option would be for the coder to "make the call" as to how readable/explicit the code actually is. This is typical the case anyway, since any coder can write obfuscated code regardless of the language :) The question is then "how much different is it to add or omit the 'return' keyword?" ... personally, I think it makes a big difference; so does Walter, apparently. Thus, if we had a choice of writing these three options: I think the later should be dropped, and the former two supported :)I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.I use the { return ...; } very much. And I like it very much. And I fully agree that the return..; is annoying, because if often use it for a search criteria or something like that. The syntax { singlestatement } would be pretty cool. But leaving the braces completely out it terrible. And {} is really not too much to type. Imagine the terrible bugs that will occur with that. I think one of the biggest advantages of D vs. C++ is the readability. That is so important. Really. Please don't destroy that.
Aug 21 2006
kris wrote:Frank Benoit wrote:You have a typo in the 3rd sample, it should read: ... which clearly shows that the 3rd option isn't the preferred one ;) -- Tomasz StachowiakI think the new sugar works really well where the code in question is *designed* around the notion of callbacks. The cues are already solidly in place at that point, so it may be less of an issue there? I'm thinking specifically of the logging examples, and some fairly advanced distributed processing paradigms. However, in typical or 'mixed' D programming, the lack of cues in this latest sugar will surely become problematic. Walter suggests the author of the callee should use an appropriate name, such that overloading would (presumeably) not be an issue and the appropriate cues would be retained. One has to wonder if that is a good idea or not. If it were possible to make the '{' '}' optional, for example, then another option would be for the coder to "make the call" as to how readable/explicit the code actually is. This is typical the case anyway, since any coder can write obfuscated code regardless of the language :) The question is then "how much different is it to add or omit the 'return' keyword?" ... personally, I think it makes a big difference; so does Walter, apparently. Thus, if we had a choice of writing these three options: I think the later should be dropped, and the former two supported :)I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.I use the { return ...; } very much. And I like it very much. And I fully agree that the return..; is annoying, because if often use it for a search criteria or something like that. The syntax { singlestatement } would be pretty cool. But leaving the braces completely out it terrible. And {} is really not too much to type. Imagine the terrible bugs that will occur with that. I think one of the biggest advantages of D vs. C++ is the readability. That is so important. Really. Please don't destroy that.
Aug 21 2006
Tom S wrote:kris wrote:I'm not sure I'd want to do away with option 3 unless option 2 supported multiple statements and a void return in a way that made sense. SeanFrank Benoit wrote:You have a typo in the 3rd sample, it should read: ... which clearly shows that the 3rd option isn't the preferred one ;)I think the new sugar works really well where the code in question is *designed* around the notion of callbacks. The cues are already solidly in place at that point, so it may be less of an issue there? I'm thinking specifically of the logging examples, and some fairly advanced distributed processing paradigms. However, in typical or 'mixed' D programming, the lack of cues in this latest sugar will surely become problematic. Walter suggests the author of the callee should use an appropriate name, such that overloading would (presumeably) not be an issue and the appropriate cues would be retained. One has to wonder if that is a good idea or not. If it were possible to make the '{' '}' optional, for example, then another option would be for the coder to "make the call" as to how readable/explicit the code actually is. This is typical the case anyway, since any coder can write obfuscated code regardless of the language :) The question is then "how much different is it to add or omit the 'return' keyword?" ... personally, I think it makes a big difference; so does Walter, apparently. Thus, if we had a choice of writing these three options: I think the later should be dropped, and the former two supported :)I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.I use the { return ...; } very much. And I like it very much. And I fully agree that the return..; is annoying, because if often use it for a search criteria or something like that. The syntax { singlestatement } would be pretty cool. But leaving the braces completely out it terrible. And {} is really not too much to type. Imagine the terrible bugs that will occur with that. I think one of the biggest advantages of D vs. C++ is the readability. That is so important. Really. Please don't destroy that.
Aug 21 2006
Sean Kelly wrote:Tom S wrote:so, perhaps, just perhaps, it's worth introducing an operator (*gasp*) specifically to make this completely unambiguous? For example: where ' ' (or whatever) would be a low-priority operator ?kris wrote:I'm not sure I'd want to do away with option 3 unless option 2 supported multiple statements and a void return in a way that made sense. SeanFrank Benoit wrote:You have a typo in the 3rd sample, it should read: ... which clearly shows that the 3rd option isn't the preferred one ;)I think the new sugar works really well where the code in question is *designed* around the notion of callbacks. The cues are already solidly in place at that point, so it may be less of an issue there? I'm thinking specifically of the logging examples, and some fairly advanced distributed processing paradigms. However, in typical or 'mixed' D programming, the lack of cues in this latest sugar will surely become problematic. Walter suggests the author of the callee should use an appropriate name, such that overloading would (presumeably) not be an issue and the appropriate cues would be retained. One has to wonder if that is a good idea or not. If it were possible to make the '{' '}' optional, for example, then another option would be for the coder to "make the call" as to how readable/explicit the code actually is. This is typical the case anyway, since any coder can write obfuscated code regardless of the language :) The question is then "how much different is it to add or omit the 'return' keyword?" ... personally, I think it makes a big difference; so does Walter, apparently. Thus, if we had a choice of writing these three options: I think the later should be dropped, and the former two supported :)I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.I use the { return ...; } very much. And I like it very much. And I fully agree that the return..; is annoying, because if often use it for a search criteria or something like that. The syntax { singlestatement } would be pretty cool. But leaving the braces completely out it terrible. And {} is really not too much to type. Imagine the terrible bugs that will occur with that. I think one of the biggest advantages of D vs. C++ is the readability. That is so important. Really. Please don't destroy that.
Aug 21 2006
If it were possible to make the '{' '}' optional, for example, then another option would be for the coder to "make the call" as to how readable/explicit the code actually is. This is typical the case anyway, since any coder can write obfuscated code regardless of the language :)Writing it without {} will always be obfuscating, if the reader doesn't know what will happen from the function names. And a good function name does perhaps not imply a delegate call to another person reading the code. The {} can make that clear, and they leave the user the choice to call the overloaded function without lazy eval.
Aug 21 2006
On Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:Frank Benoit wrote:Would it possible to use ... func ( cast(char[]) "abc" ); to force the compiler to chose 'func( char[] a)' instead of the delgated version? -- Derek Parnell Melbourne, Australia "Down with mediocrity!"I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg )
Aug 21 2006
Derek Parnell wrote:On Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:arghhh!!! Please ... cast() is only for exceptional circumstances :(Frank Benoit wrote:Would it possible to use ... func ( cast(char[]) "abc" ); to force the compiler to chose 'func( char[] a)' instead of the delgated version?I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg )
Aug 21 2006
kris wrote:Derek Parnell wrote:cast( :-) ) ( arghhh!!! Please ... cast() is only for exceptional circumstances :( );On Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:arghhh!!! Please ... cast() is only for exceptional circumstances :(Frank Benoit wrote:Would it possible to use ... func ( cast(char[]) "abc" ); to force the compiler to chose 'func( char[] a)' instead of the delgated version?I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg )
Aug 21 2006
On Mon, 21 Aug 2006 15:16:34 -0700, kris wrote:Derek Parnell wrote:And this situation is not exceptional? Okay, than how about a keyword ... func ( forget_the_delegate_and_use_the_other_type_instead "abc" ); ;-) -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 22/08/2006 10:43:28 AMOn Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:arghhh!!! Please ... cast() is only for exceptional circumstances :(Frank Benoit wrote:Would it possible to use ... func ( cast(char[]) "abc" ); to force the compiler to chose 'func( char[] a)' instead of the delgated version?I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg )
Aug 21 2006
Derek Parnell wrote:On Mon, 21 Aug 2006 14:18:04 -0700, Walter Bright wrote:As for ambiguity between overloads, doesn't it make more sense to change the overloading rules so that 'func( char[] a)' takes precedence, if this can be done? I'd think that it would make sense as it is similar to how implicit conversions for integral types work. The {} syntax would then 'force' the second form to be taken, but this is no cast. Then again, I could be very wrong.Frank Benoit wrote:Would it possible to use ... func ( cast(char[]) "abc" ); to force the compiler to chose 'func( char[] a)' instead of the delgated version?I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout. There also is no syntactic clue what the function *does*. One must look at the function interface and documentation to use it successfully anyway. It's going to take some caution to use this capability in a productive way.There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg )
Aug 21 2006
Walter Bright wrote:Frank Benoit wrote:A feature request that pops up now and then is to at least allow using the in/out/inout keywords at call site, just to help document the code. That has analogs with the current discussion, but with delegates, we already have the option of explicitly using a {return...;} delegate where it makes sense. Possibly, we might see coding guidelines one day, saying that one should write: func(/*lazy*/ i++); where the laziness has a semantic difference. I think it is a good idea to document your code as such, but the fraction of functions using lazy evaluation will probably be low, and the number of cases where the laziness of the function argument will have side-effects will be even lower.I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?It's true there is no clue from the user's side which it is. But there also isn't a clue whether the arguments are in, out, or inout.Yes. I think the additional typing makes the feature seem more complex. In our search for solutions, we favor the most simple ones. By reducing the apparent complexity of a feature, it is much more likely to be used. /Oskarfunc( "abc" ) calls func( char[] a ) func({ "abc" }) calls func( char[] delegate() dg ) func({ return "abc"; }) calls func( char[] delegate() dg ) With that syntax one can immidiatly see "this is a delegate, if it is called or not or more than once depends on func()" and the more typing of {} is not too much.I agree with you that replacing exp with { return exp; } is clear and not much additional typing. But I've discovered over and over to my surprise that the additional typing causes people to not realize that D has that capability. The extra typing simply kills it.
Aug 22 2006
While I partly agree with you, Frank, I don't want to dismiss this new feature too soon. So let me disagree a bit ;) Frank Benoit wrote:There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );That is a serious problem as it breaks existing code. But if these functions were written with lazy evaluation in mind, there would be no sense in having such two overloads. The coder would ensure that the expression is evaluated just once. In order to overcome the problem with code having both func(Type) and func(Type delegate()) overloads, but not really meaning them to be used with lazy expression evaluation, the coder would have to make it straight that lazy evaluation is preferred in the remaining cases. One possible method to accomplish it is by adding a new storage specifier, 'lazy' to the bunch of 'in', 'inout' and 'out'. The consequences of such an approach would be: ---- void foo(lazy int x) { static assert(is(typeof(x) : int delegate())); writefln(x()); } void foo(int x) { writefln(x); } --- Error: foo(lazy int) conflicts with foo(int) ---- void foo(lazy int x) { static assert(is(typeof(x) : int delegate())); writefln(x()); } void bar(int delegate() x) { foo(x); } void bar(int x) { writefln(x); } foo({return 5;}); // foo(lazy int) called foo(5 + 5); // foo(lazy int) called, expression evaluated lazily bar(5); // bar(int) called foo(5 + 5); // bar(int) called, expression evaluated at call-time bar({return 5;}); // bar(int delegate()) called ---- Which really means that lazy args and delegates could be converted between each other without explicit casts, but in order to make a function accept lazily-evaluated expressions, the programmer would have to declare his/her intent clearly.The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once?Just as Walter stated, that's the same case as with 'in' vs 'inout' vs 'out' arguments. You don't know what will happen in either case -- Tomasz Stachowiak
Aug 21 2006
Just as Walter stated, that's the same case as with 'in' vs 'inout' vs 'out' arguments. You don't know what will happen in either caseIf an argument is in/out/inout it is clear from the content and context. If it is out, you call the function to get something. lazy-eval syntax When I code, I often guess what the method is called and what arguments it has. For example i have a container with a add( element ) method. Now this method is implemented with lazy-eval, if the container is already full, the argument is not evaluated. Or the get( index ) method does not eval the arg if the container is empty. And every time I have not only to remember the method name and arguments, now I have to know details of the implementation. Great, that was the silly thing they wanted to take away from me with this OOP ;) Solution: Force the user the make {} around a delegate argument. Now I get a compiler error when writing container.get(i++); Then I know, this is a delegate, take care with the increment, writing container.get({i}); i++;
Aug 21 2006
Frank Benoit wrote:Nah, just that the arg might be left not-evaluated... I wouldn't use it with functions like 'get' or 'set' but with stuff for which it makes sense, just like with 'assert'. I'm just trying to defend a point I don't really believe... You're probably right, the implicit approach brings more traps than advantages :( -- Tomasz StachowiakJust as Walter stated, that's the same case as with 'in' vs 'inout' vs 'out' arguments. You don't know what will happen in either caseIf an argument is in/out/inout it is clear from the content and context. If it is out, you call the function to get something. lazy-eval syntax When I code, I often guess what the method is called and what arguments it has. For example i have a container with a add( element ) method. Now this method is implemented with lazy-eval, if the container is already full, the argument is not evaluated. Or the get( index ) method does not eval the arg if the container is empty. And every time I have not only to remember the method name and arguments, now I have to know details of the implementation.
Aug 21 2006
I'm just trying to defend a point I don't really believe... You're probably right, the implicit approach brings more traps than advantages :(Yes, and that is my only point. delegate arguments will become a often used feature. Well, I like to use them since 0.163. But without making them explicit, they will make D to hell. I see me sitting at night, staring at the same code, and after hours of debugging "NOOOO, its a delegate!!!"
Aug 21 2006
Frank Benoit wrote:For example i have a container with a add( element ) method. Now this method is implemented with lazy-eval, if the container is already full, the argument is not evaluated. Or the get( index ) method does not eval the arg if the container is empty. And every time I have not only to remember the method name and arguments, now I have to know details of the implementation. Great, that was the silly thing they wanted to take away from me with this OOP ;) Solution: Force the user the make {} around a delegate argument. Now I get a compiler error when writing container.get(i++); Then I know, this is a delegate, take care with the increment, writing container.get({i}); i++;While I understand your concern, I don't think the examples illustrate it. If a get() is done for an element that doesn't exist, the usual way to deal with it is throw an exception, and have some complex scheme to try and undo the side effects from evaluating the argument. With lazy evaluation, no need to undo the side effects, as only if the get() is guaranteed to succeed will the argument be evaluated. The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
Aug 21 2006
Walter Bright wrote:Frank Benoit wrote:Can you at least make it optional? Or, can you come up with an operator or something that is low-impact? Making this unambiguous instead would surely be a win-win?For example i have a container with a add( element ) method. Now this method is implemented with lazy-eval, if the container is already full, the argument is not evaluated. Or the get( index ) method does not eval the arg if the container is empty. And every time I have not only to remember the method name and arguments, now I have to know details of the implementation. Great, that was the silly thing they wanted to take away from me with this OOP ;) Solution: Force the user the make {} around a delegate argument. Now I get a compiler error when writing container.get(i++); Then I know, this is a delegate, take care with the increment, writing container.get({i}); i++;While I understand your concern, I don't think the examples illustrate it. If a get() is done for an element that doesn't exist, the usual way to deal with it is throw an exception, and have some complex scheme to try and undo the side effects from evaluating the argument. With lazy evaluation, no need to undo the side effects, as only if the get() is guaranteed to succeed will the argument be evaluated. The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
Aug 21 2006
The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.I am a programmer. I like them. I like to understand my and foreign code. I hate C++ for that reason. All the time i have to look in hundred files and have thousands of informations to care about only to get a clue of what is going on. Implicit or explicit, this is really an important step. A decision between readability in big projects and nice looking example code. http://www.digitalmars.com/d/overview.html Who D is For Teams who write apps with a million lines of code in it. Look at the example with explicit delegates: void foo() { int v = 2; cond ({ scase(v == 1, {writefln("it is 1")}), scase(v == 2, {writefln("it is 2")}), scase(v == 3, {writefln("it is 3")}), scase(true, {writefln("it is the default")}) }); } Why should one not like it? It looks sexy ;D
Aug 21 2006
On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Frank Benoit wrote:Huh? You asked them all? You didn't ask me and I like it. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 22/08/2006 10:50:15 AMFor example i have a container with a add( element ) method. Now this method is implemented with lazy-eval, if the container is already full, the argument is not evaluated. Or the get( index ) method does not eval the arg if the container is empty. And every time I have not only to remember the method name and arguments, now I have to know details of the implementation. Great, that was the silly thing they wanted to take away from me with this OOP ;) Solution: Force the user the make {} around a delegate argument. Now I get a compiler error when writing container.get(i++); Then I know, this is a delegate, take care with the increment, writing container.get({i}); i++;While I understand your concern, I don't think the examples illustrate it. If a get() is done for an element that doesn't exist, the usual way to deal with it is throw an exception, and have some complex scheme to try and undo the side effects from evaluating the argument. With lazy evaluation, no need to undo the side effects, as only if the get() is guaranteed to succeed will the argument be evaluated. The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.
Aug 21 2006
Derek Parnell wrote:On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses. C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates. Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.
Aug 21 2006
On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright wrote:Derek Parnell wrote:Yes. However I couldn't release it in Build until GDC caught up to the ability, so I took it out again.On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165?The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz.I didn't.I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.And so am I, but now I *also* wary of the pitfalls that this implicit conversion to delegates open up.C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.And do I give a toss about C++ programming foibles ;-)Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.Not sure about that. I see the problem with existing calls to functions suddenly behaving differently with no apparent reason. With this new ability, a library writer can change the interface and my program will still compile, but may now behave in unexpected ways. Let's assume the original API said void SomeFunc(char[] x); so I code ... void xyzzy(inout x) { char[] r = std.string.format("%d", x); x++; } . . . int y = 7; SomeFunc( std.string.format("The number is " ~ xyzzy(y)) ); Then the library writer changes this to void SomeFunc(char[] delegate() x); My code still compiles but there is now no guarantee that my xyzzy() function will be called. The API function may chose to not call it for some reason valid to itself. But I'm left scratching my head trying to work out why my variable 'y' is sometimes being updated and other times not. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 22/08/2006 12:49:40 PM
Aug 21 2006
Derek Parnell wrote:On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright wrote:Ok.Derek Parnell wrote:Yes. However I couldn't release it in Build until GDC caught up to the ability, so I took it out again.On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165?The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz.I didn't.It's good to be wary of unusual new features. There are usually unanticipated problems with them.I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.And so am I, but now I *also* wary of the pitfalls that this implicit conversion to delegates open up.LOL! But expression templates, horrible hack that they are, are often listed as the reason C++ is so powerful and therefore why should one change? Expression templates are how C++ does "domain specific languages" and David Abraham explains them to packed conference rooms. So I believe there is a serious demand for them, but not the way C++ does it, as probably only 5 people on the planet are able to create a DNS using them. With the lazy evaluation thing, though, writing DNSs becomes simple and straightforward, which may (just may) catapult D forward like defmac rescued Lisp from oblivion.C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.And do I give a toss about C++ programming foibles ;-)Not sure about that. I see the problem with existing calls to functions suddenly behaving differently with no apparent reason. With this new ability, a library writer can change the interface and my program will still compile, but may now behave in unexpected ways.He can do that anyway - I've already enumerated several ways. But there's been an awful lot of threads here that boil down to adding more information to the function declaration so that it's easier for the user to see what is happening. (And it's a little hard to hide a delegate declaration!) I should also point out that: void foo(int x); and: void foo(int delegate() x); will have different name mangling. So if you change the library, and don't recompile the user code, it won't link.Let's assume the original API said void SomeFunc(char[] x); so I code ... void xyzzy(inout x) { char[] r = std.string.format("%d", x); x++; } . . . int y = 7; SomeFunc( std.string.format("The number is " ~ xyzzy(y)) ); Then the library writer changes this to void SomeFunc(char[] delegate() x); My code still compiles but there is now no guarantee that my xyzzy() function will be called. The API function may chose to not call it for some reason valid to itself. But I'm left scratching my head trying to work out why my variable 'y' is sometimes being updated and other times not.Suppose there's an API function: void anotherFunc(int x); which I call: int y = 3; anotherFunc(y); writefln(y); then the library writer changes the API to: void anotherFunc(inout int x); and internally increments x. I'm left wondering why 4 is now being printed instead of 3. Changing an API and thereby breaking (obviously or subtly) the user code is as old as programming ("DLL hell" is a term probably older than many programmers!). The only answer I can think of is, if you must change the API of a function, and you have legacy users of it, give the changed one a new name and tag the old name with 'deprecated'.
Aug 21 2006
On Mon, 21 Aug 2006 20:54:36 -0700, Walter Bright wrote: By the way, are you also saying that given two functions ... foo(T x); and: foo(T delegate() x); that the delegate signature will *always* be called rather than the non-delegate signature when calling with a 'T' argument?Let's assume the original API saidSuppose there's an API function:I guess my point is that this new feature increases the number of ways we can inadvertently stuff things up. So is the benefit going to outweigh the cost? Obviously its too early to know the answer to that yet. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 22/08/2006 4:31:02 PM
Aug 21 2006
Derek Parnell wrote:On Mon, 21 Aug 2006 20:54:36 -0700, Walter Bright wrote: By the way, are you also saying that given two functions ... foo(T x); and: foo(T delegate() x); that the delegate signature will *always* be called rather than the non-delegate signature when calling with a 'T' argument?No. It'll be an ambiguity error.I agree.I guess my point is that this new feature increases the number of ways we can inadvertently stuff things up. So is the benefit going to outweigh the cost? Obviously its too early to know the answer to that yet.Let's assume the original API saidSuppose there's an API function:
Aug 22 2006
Walter Bright wrote:Derek Parnell wrote:In my limited experience, the attendees of those conferences are what I'd consider "typical" C++ programmers--they tend to use a fairly limited subset of the language's features and their code probably looks like what was popular maybe 15 years ago. Templates are a fairly new thing to many of them, and many are even just getting their feet wet with the STL. I would guess that Dave's presentation on expression templates draws a crowd for a few simple reasons: people have heard of Dave because he's written a book and is the front man for Boost in many respects, and because expression templates have a high "gee whiz" factor. I think that people simply interested in learning how to write expression templates would go out and buy a copy of "C++ Templates: The Complete Guide" by D. Vandevoorde and N. Josuttis and probably not bother with the lecture at all.On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright wrote:LOL! But expression templates, horrible hack that they are, are often listed as the reason C++ is so powerful and therefore why should one change? Expression templates are how C++ does "domain specific languages" and David Abraham explains them to packed conference rooms. So I believe there is a serious demand for them, but not the way C++ does it, as probably only 5 people on the planet are able to create a DNS using them.Derek Parnell wrote: C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.And do I give a toss about C++ programming foibles ;-)With the lazy evaluation thing, though, writing DNSs becomes simple and straightforward, which may (just may) catapult D forward like defmac rescued Lisp from oblivion.It was simple and straightforward using the previous syntax as well. Though I'll grant that the "return" bit took up a bit of horizontal screen real estate that some might not find appealing.Suppose there's an API function: void anotherFunc(int x); which I call: int y = 3; anotherFunc(y); writefln(y); then the library writer changes the API to: void anotherFunc(inout int x); and internally increments x. I'm left wondering why 4 is now being printed instead of 3.But that would be a blatant change in behavior for the API function, while converting to a delegate parameter is arguably not a change in behavior, just in the way the value is being transferred. It's probably also worth noting that in a language supporting const features, switching an in param to inout would probably result in compile errors in at least a few uses of the function. That aside, it may be worth noting that this new delegate syntax has some small potential for faking const behavior in D, though it would likely make a mess of code attempting this. Sean
Aug 22 2006
Walter Bright wrote:Changing an API and thereby breaking (obviously or subtly) the user code is as old as programming ("DLL hell" is a term probably older than many programmers!). The only answer I can think of is, if you must change the API of a function, and you have legacy users of it, give the changed one a new name and tag the old name with 'deprecated'.The problem isn't that changing things, breaks stuff. It it that going from (T) to (T delegate()) is almost always valid, but the programmer can *never* assume that it is. Thinking of it that way, changing the API from one to the other is *never* safe. This means that using a T delegate() as a parameter might as well be considered an unsafe practice. If you started with just T, your locked in for all time. Starting with T delegate() is almost as bad. Having a feature that is nearly impossible to use safely may well be worse than not having the feature.
Aug 22 2006
BCS wrote:Thinking of it that way, changing the API from one to the other is *never* safe. This means that using a T delegate() as a parameter might as well be considered an unsafe practice. If you started with just T, your locked in for all time. Starting with T delegate() is almost as bad. Having a feature that is nearly impossible to use safely may well be worse than not having the feature.Changing the API is always problematic, including with traditional features like implicit conversions, inout, etc. As I posted before, the pragmatic way to change an API is to create a new name with the new interface, and deprecate the old one. This is not a problem that is new or unique to delegate conversions. It's as old as programming. As for delegate conversions being nearly impossible to use safely, I don't understand that at all. I would agree that it is a probably unique feature for C like languages, and hence people will perhaps make mistakes with it until it becomes familiar, expected, and passe.
Aug 22 2006
On Mon, 21 Aug 2006 19:41:24 -0700, Walter Bright <newshound digitalmars.com> wrote:Derek Parnell wrote:Zzzz? I really don't think that was the response. I believe most people here were quite excited about the { } form when it came out (especially after experimenting with it for a time). They just may not have expressed it in your hearing (ie. in this newsgroup where you are so accustomed to feedback). Drop in the #D IRC channel on freenode sometime. You might see more discussion about the language then you imagine. I've run across many people that were overjoyed with {} feature, including long time D users here. Silence doesn't mean that there's no support for a feature. In the context of D, silence really is a good sign. As you know, most people here are quite outspoken about any little feature added that might not be consistant with the D language. :) -JJROn Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.
Aug 21 2006
Walter Bright wrote:Derek Parnell wrote:I did, and continue to do so, along with a number of others I know working on delegate-oriented systems. What was unweidly about the prior syntax was simply the return and the 'superfluous' semicolons. The braces don't get in the way at all, and in fact, make it quite clear exactly what is going on. As it stands now, blatant ambiguities have been introduced. And for what?On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.Eh. Callbacks have been around for decades. Syntax to support that is good or bad depending upon language you choose.Perhaps what is so off-putting of this feature, as opposed to out, inout, const, implicit conversions, and other effects that require one to look at the interface, is it's very unusual (even unique?) for a C-like language.Perhaps, but that doesn't mean the syntax needs to to be ambiguous. Does it? We all know that perfectly well.
Aug 21 2006
kris wrote:I wasn't thinking of callbacks. Take a look at the common practice: #define log_printf if (logging) printf e; and the endless variations on it, all trying to do lazy evaluation of e. Expression templates are just the latest method.C++ programmers have been trying to do this for over a decade - first with preprocessor macros, and now with expression templates.Eh. Callbacks have been around for decades. Syntax to support that is good or bad depending upon language you choose.
Aug 21 2006
On Mon, 21 Aug 2006 22:41:24 -0400, Walter Bright <newshound digitalmars.com> wrote:Derek Parnell wrote:I very much like the { } syntax, it was just surprising to me. The first piece of code I attempted to compile after installing DMD v0.165 choked on this new no-brace feature. tabs.d(120): function dfl.application.Application.run called with argument types: (MainForm) matches both: dfl.application.Application.run(void delegate()) and: dfl.application.Application.run(Form) MainForm is derived from Form; it doesn't exactly match with Form so implicit matching rules kick in. Somehow MainForm turned into a delegate returning void, which even seems like 2 levels of implicit matching, when Form is obviously the best match. The previously noted foo(bar++) doesn't say how many times bar is incremented, if at all. Perhaps there could be a compromise in syntax: require { } but if there's just one expression in it, it can become the return value and type: foo({bar++}); I'm not sure if a semicolon after the expression would be needed or cause problems. Back on function parameter matching rules, not having more than 2 levels of matching rules (exact / implicit across the board) sucks in many cases. It very much hinders the programmer when I don't think it even needs the complexity of C++'s matching rules to be improved. As long as there is a well-defined set of rules, it should be fine to have a few levels of matching rules. Examples: * A derived class should match moreso with its base class. * When one argument doesn't match exactly, the rest of the arguments shouldn't automatically kick into implicit mode too. I'm sure there are more; I've had to have a bunch of workarounds along the way and forgot about them. If interested, I'll try harder to find them.On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote:Did you use it before 0.165? Did anyone? Everyone I'd show the { } syntax to reacted with zzzzzz. I show them the one without, and all of a sudden they are excited about it and can think of all kinds of uses.The problem with requiring the { } around the argument is that programmers just don't like it. I don't think I can make them like it.Huh? You asked them all? You didn't ask me and I like it.
Aug 21 2006
Chris Miller wrote:I very much like the { } syntax, it was just surprising to me. The first piece of code I attempted to compile after installing DMD v0.165 choked on this new no-brace feature. tabs.d(120): function dfl.application.Application.run called with argument types: (MainForm) matches both: dfl.application.Application.run(void delegate()) and: dfl.application.Application.run(Form) MainForm is derived from Form; it doesn't exactly match with Form so implicit matching rules kick in. Somehow MainForm turned into a delegate returning void, which even seems like 2 levels of implicit matching, when Form is obviously the best match.A cast of a value to: void delegate() is a match with conversions, as is a conversion of a derived class to a base class. Hence the ambiguity error.The previously noted foo(bar++) doesn't say how many times bar is incremented, if at all.It's once if foo's parameter is not a delegate. If it is a delegate, it is defined by the person who implemented foo().Perhaps there could be a compromise in syntax: require { } but if there's just one expression in it, it can become the return value and type: foo({bar++}); I'm not sure if a semicolon after the expression would be needed or cause problems. Back on function parameter matching rules, not having more than 2 levels of matching rules (exact / implicit across the board) sucks in many cases. It very much hinders the programmer when I don't think it even needs the complexity of C++'s matching rules to be improved. As long as there is a well-defined set of rules, it should be fine to have a few levels of matching rules. Examples: * A derived class should match moreso with its base class. * When one argument doesn't match exactly, the rest of the arguments shouldn't automatically kick into implicit mode too. I'm sure there are more; I've had to have a bunch of workarounds along the way and forgot about them. If interested, I'll try harder to find them.I admit there's a strong temptation to add more levels. But I don't think it's so clear which matches are obviously "better", and the more levels there are the more interactions there are, especially when multiple arguments are in play. With the current 2 level system, it can be frustrating, but it may in the end be beneficial by forcing one to be clear about which function will get called.
Aug 21 2006
Other people already proposed the { expr } syntax, I liked it too, can't we think of it like this ... We usually write compound statement as: { statement1; statement2; return expr; } We can ignore ';' if there is only statement, so following is valid: { statement } { return expr } If there is only a return statement, and the return type of the delegate matches the expression, we can make the 'return' keyword optional: { expr } well ... what do you all say ? (hope Walter likes this) Sai
Aug 22 2006
Sai wrote:Other people already proposed the { expr } syntax, I liked it too, can't we think of it like this ... We usually write compound statement as: { statement1; statement2; return expr; } We can ignore ';' if there is only statement, so following is valid: { statement } { return expr } If there is only a return statement, and the return type of the delegate matches the expression, we can make the 'return' keyword optional: { expr } well ... what do you all say ? (hope Walter likes this) SaiSure is better than deliberately introducing ambiguity into the language.
Aug 22 2006
Frank Benoit wrote:I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once? There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt ); It would be really important for me to have readable code. I want to look at the code, and want to have an impression what will happen. What really would help, is if the the delegate is marked in some way as such. In the last releases of DMD the {} syntax was there. With it you needed the return statement. Perhaps we can choose the {} syntax with an optional return statement.... { "abc" } => char[] delegate() { return "abc"; } => char[] delegate() func( "abc" ) calls func( char[] a ) func({ "abc" }) calls func( char[] delegate() dg ) func({ return "abc"; }) calls func( char[] delegate() dg ) With that syntax one can immidiatly see "this is a delegate, if it is called or not or more than once depends on func()" and the more typing of {} is not too much.So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc The D version of a logger has five 'levels' of logging: log.trace (char[]) log.info (char[]) log.warn (char[]) log.error (char[]) log.fatal (char[]) and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction: log.trace (char[] delegate() dg) log.info (char[] delegate() dg) log.warn (char[] delegate() dg) log.error (char[] delegate() dg) log.fatal (char[] delegate() dg) The support code was installed fairly recently, and the above delegate-wrappers were on the todo list. However, dmd.165 will presumeably not compile this code? Won't there be an overload ambiguity? If so, it illustrates a serious shortcoming in the new syntax
Aug 21 2006
kris wrote:So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc The D version of a logger has five 'levels' of logging: log.trace (char[]) log.info (char[]) log.warn (char[]) log.error (char[]) log.fatal (char[]) and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction: log.trace (char[] delegate() dg) log.info (char[] delegate() dg) log.warn (char[] delegate() dg) log.error (char[] delegate() dg) log.fatal (char[] delegate() dg) The support code was installed fairly recently, and the above delegate-wrappers were on the todo list. However, dmd.165 will presumeably not compile this code?Correct.Won't there be an overload ambiguity?Yes.If so, it illustrates a serious shortcoming in the new syntaxJust get rid of the (char[]) versions. You could argue "what about the efficiency?" 1) Passing a delegate is exactly the same number of instructions as passing a char[], i.e., it is two values being passed. 2) Actually calling the dg() will, of course, cost more instructions than just referencing a []. This is mitigated by, presumably, logging being normally off, and being overshadowed by the rest of the actual logging cost. 3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.
Aug 21 2006
Walter Bright wrote:kris wrote:lol! I can just imagine 1001 log statements inlined within the log code ;D There's a fairly significant overhead in making a callback. Just as much, or more, than handling a significant proportion of log-messages themselves (I find a significant number to be static text; not contructed dynamically). In addition, if the compiler can't prove these logging delegate do not escape, each hosting function may well have their frames allocated on the heap ... we've been discussing that just recently. All this because you insist programmers can't be bothered to add a tiny bit of syntax (even optionally) to clarify the intent? This is madness! Utter madness. Please restore some sanity here. Why don't you conduct a poll asking exactly which 'programmers' won't use the {} delimeters to unambiguously declare a delegate?So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc The D version of a logger has five 'levels' of logging: log.trace (char[]) log.info (char[]) log.warn (char[]) log.error (char[]) log.fatal (char[]) and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction: log.trace (char[] delegate() dg) log.info (char[] delegate() dg) log.warn (char[] delegate() dg) log.error (char[] delegate() dg) log.fatal (char[] delegate() dg) The support code was installed fairly recently, and the above delegate-wrappers were on the todo list. However, dmd.165 will presumeably not compile this code?Correct.Won't there be an overload ambiguity?Yes.If so, it illustrates a serious shortcoming in the new syntaxJust get rid of the (char[]) versions. You could argue "what about the efficiency?" 1) Passing a delegate is exactly the same number of instructions as passing a char[], i.e., it is two values being passed. 2) Actually calling the dg() will, of course, cost more instructions than just referencing a []. This is mitigated by, presumably, logging being normally off, and being overshadowed by the rest of the actual logging cost. 3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.
Aug 21 2006
kris wrote:Walter Bright wrote:I've been thinking a lot about the escape problem. I'm pretty sure that: char[] delegate() { return "foo"; } can be detected and so I can assure you it won't cause the enclosing function's variables to be allocated on the heap.Just get rid of the (char[]) versions. You could argue "what about the efficiency?" 1) Passing a delegate is exactly the same number of instructions as passing a char[], i.e., it is two values being passed. 2) Actually calling the dg() will, of course, cost more instructions than just referencing a []. This is mitigated by, presumably, logging being normally off, and being overshadowed by the rest of the actual logging cost. 3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.lol! I can just imagine 1001 log statements inlined within the log code ;D There's a fairly significant overhead in making a callback. Just as much, or more, than handling a significant proportion of log-messages themselves (I find a significant number to be static text; not contructed dynamically). In addition, if the compiler can't prove these logging delegate do not escape, each hosting function may well have their frames allocated on the heap ... we've been discussing that just recently.All this because you insist programmers can't be bothered to add a tiny bit of syntax (even optionally) to clarify the intent? This is madness! Utter madness. Please restore some sanity here.Calling it 'madness' is blowing things way out of proportion. (And you still can use the { } syntax like before.)Why don't you conduct a poll asking exactly which 'programmers' won't use the {} delimeters to unambiguously declare a delegate?I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen. It's sort of like the itunes. Apple didn't invent anything new. They just removed a couple annoying button pushes, and voila, suddenly the handheld music player gained traction. I hesitate to use argumentum ad verecundiam because it's a logical fallacy, so you can take the following for what it's worth. Andrei and I certainly have our differences of opinion. But when I disagree with him, I'd better have done my homework, or I'll get cut to pieces. He thinks (and he obviously convinced me) that removing the { } makes all the difference. I want to give it a fair shot. It has potential to be a big win for D, and the consequences of it being a mistake are small. How many here have experience with defmac in Common Lisp, or something equivalent? I sure don't, but people who use CL successfully say it's huge.
Aug 21 2006
Walter Bright wrote:kris wrote:Asserting "programmers won't type the {}" as the reason for deliberate introduction of ambiguous syntax is throwing caution to wind; in the extreme. Any for what? There's not even any evidence to uphold that assertion. You didn't even bother with a poll of the people who actually use delegates ... To me, that's madness. Marketing is clearly in the driving seat, and you apparently won't even consider other options.Walter Bright wrote:I've been thinking a lot about the escape problem. I'm pretty sure that: char[] delegate() { return "foo"; } can be detected and so I can assure you it won't cause the enclosing function's variables to be allocated on the heap.Just get rid of the (char[]) versions. You could argue "what about the efficiency?" 1) Passing a delegate is exactly the same number of instructions as passing a char[], i.e., it is two values being passed. 2) Actually calling the dg() will, of course, cost more instructions than just referencing a []. This is mitigated by, presumably, logging being normally off, and being overshadowed by the rest of the actual logging cost. 3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.lol! I can just imagine 1001 log statements inlined within the log code ;D There's a fairly significant overhead in making a callback. Just as much, or more, than handling a significant proportion of log-messages themselves (I find a significant number to be static text; not contructed dynamically). In addition, if the compiler can't prove these logging delegate do not escape, each hosting function may well have their frames allocated on the heap ... we've been discussing that just recently.All this because you insist programmers can't be bothered to add a tiny bit of syntax (even optionally) to clarify the intent? This is madness! Utter madness. Please restore some sanity here.Calling it 'madness' is blowing things way out of proportion. (And you still can use the { } syntax like before.)How do you know this? Where exactly does your information come from, and how is it measured?Why don't you conduct a poll asking exactly which 'programmers' won't use the {} delimeters to unambiguously declare a delegate?I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen.It's sort of like the itunes. Apple didn't invent anything new. They just removed a couple annoying button pushes, and voila, suddenly the handheld music player gained traction. I hesitate to use argumentum ad verecundiam because it's a logical fallacy, so you can take the following for what it's worth. Andrei and I certainly have our differences of opinion. But when I disagree with him, I'd better have done my homework, or I'll get cut to pieces. He thinks (and he obviously convinced me) that removing the { } makes all the difference.Perhaps Andrei doesn't write much D, or perhaps he's just wrong? Either way, you've deliberately introduced a serious syntactic ambiguity. That's going to cause notable detractment, and for good reason.I want to give it a fair shot. It has potential to be a big win for D, and the consequences of it being a mistake are small. How many here have experience with defmac in Common Lisp, or something equivalent? I sure don't, but people who use CL successfully say it's huge.It is, but you don't lose anything in usability by typing the curly parens (or some other operator/indicator). Plus the ambiguity would not exist. Some say Paris Hilton is "famous for being famous" -- perhaps one of the most empty accomplishments one could possibly achieve. What we apparently have here is ambiguity for the sake of ambiguity. Notice the similarity? This is *not* breaking new ground, Walter -- it's only going to break code. You can resolve that simply and easily with a minor adjustment.
Aug 22 2006
Walter Bright wrote:I've been thinking a lot about the escape problem. I'm pretty sure that: char[] delegate() { return "foo"; } can be detected and so I can assure you it won't cause the enclosing function's variables to be allocated on the heap.And what about those cases where the expression is somewhat more complex? Will you revert all this ambiguity when a bogus heap-frame is demonstrated? One where the intent was never to create a delegate, but simply to evaluate an argument expr instead? Or will you insist that a smarter detector is the solution? One has to suspect that there are far more productive ways to take risks with a language than this one: the latter is not only unambiguous, it even /looks/ like a delegate. The former? Who knows what it does anymore
Aug 22 2006
kris wrote:Walter Bright wrote:It's straightforward at one level - if a delegate does not reference any variables on the enclosing functions stack frame, there's no reason to allocate it on the heap. Something very similar is done now: variables referenced by nested functions are marked so they are not allocated into registers.I've been thinking a lot about the escape problem. I'm pretty sure that: char[] delegate() { return "foo"; } can be detected and so I can assure you it won't cause the enclosing function's variables to be allocated on the heap.And what about those cases where the expression is somewhat more complex? Will you revert all this ambiguity when a bogus heap-frame is demonstrated? One where the intent was never to create a delegate, but simply to evaluate an argument expr instead? Or will you insist that a smarter detector is the solution?One has to suspect that there are far more productive ways to take risks with a language than this one: the latter is not only unambiguous, it even /looks/ like a delegate. The former? Who knows what it does anymoreWithout knowledge of what somefunc() does by looking at the declaration for it and the documentation, there's no way to know what it does anyway. If one does look at the function declaration, it's pretty obvious if it has a delegate parameter. I expect function writers to use this in a way that makes sense, not in a way to pull the rug out from under client code. At some level, you have to trust them, with or without this feature. For example, doesn't it make sense that dotimes(int n, void delegate() exp) evaluates exp n times?
Aug 22 2006
Walter Bright wrote:I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen.I've been working on a predicate-oriented algorithm module on and off for the past few weeks. A few looping constructs have been left out because foreach seems a preferable solution in D, but the rest is coming along nicely. About the only problem I had was inlining delegates passed to the functions, so the default operations are passed as structs with opCall defined instead.I hesitate to use argumentum ad verecundiam because it's a logical fallacy, so you can take the following for what it's worth. Andrei and I certainly have our differences of opinion. But when I disagree with him, I'd better have done my homework, or I'll get cut to pieces. He thinks (and he obviously convinced me) that removing the { } makes all the difference.I'll admit that doing so makes a difference to me, as it allows for tricks that aren't possible with explicit delegate signifiers. But I'm undecided as to whether this is a "good thing" or not--I plan to give it some time before I decide. I do think it's unfortunate that Kris' logging code will be unable to accept both string and delegate parameters however. And I'd like to note that all the tricks I'm thinking of for the new syntax tend to involve refactoring existing code (which is potentially dangerous, given the issue with side-effects) rather than writing new code. With new code I don't see much benefit to omitting all evidence that a parameter will be converted to a delegate, as it's something the user must be aware of to write correct code. Sean
Aug 22 2006
Sean Kelly wrote:I'll admit that doing so makes a difference to me, as it allows for tricks that aren't possible with explicit delegate signifiers. But I'm undecided as to whether this is a "good thing" or not--I plan to give it some time before I decide.Good - I don't think any of us are prescient enough to see all the ramifications of this. Nobody predicted where C++ templates wound up (the good and the bad).I do think it's unfortunate that Kris' logging code will be unable to accept both string and delegate parameters however.True, but I don't think it's a serious deficiency. At worst, Kris can define two sets of them with different names, one set with values and the other with delegate parameters.And I'd like to note that all the tricks I'm thinking of for the new syntax tend to involve refactoring existing code (which is potentially dangerous, given the issue with side-effects) rather than writing new code. With new code I don't see much benefit to omitting all evidence that a parameter will be converted to a delegate, as it's something the user must be aware of to write correct code.I can't stress this enough - one must *already* be aware of the declaration to write correct code. Implicit conversions can cause trouble, as well as in, out, inout, const, and all the other storage classes discussed. Previous discussions here frequently revolved around the need to put information in the function declaration so people have some guarantees about what the function does - const is a prime example. Another is the suggestion to move the contracts from the implementation to the declaration. There is no way to know what: somefunc(++i); does without looking at the declaration for somefunc() - even without lazy evaluation. Heck, you can't even know what ++i does without looking at the type of i. It might be a class instance with a bizarre operator overload.
Aug 22 2006
Walter Bright wrote:Sean Kelly wrote:C++ templates are well-intentioned and really quite powerful, but they're a horrible hack, even when used as originally intended. The need for "template" as a means to distinguish dependent type names is a perfect example of something that should be completely unnecessary.I'll admit that doing so makes a difference to me, as it allows for tricks that aren't possible with explicit delegate signifiers. But I'm undecided as to whether this is a "good thing" or not--I plan to give it some time before I decide.Good - I don't think any of us are prescient enough to see all the ramifications of this. Nobody predicted where C++ templates wound up (the good and the bad).In light of my opening paragraph, I really shoudn't have said "all."And I'd like to note that all the tricks I'm thinking of for the new syntax tend to involve refactoring existing code (which is potentially dangerous, given the issue with side-effects) rather than writing new code. With new code I don't see much benefit to omitting all evidence that a parameter will be converted to a delegate, as it's something the user must be aware of to write correct code.I can't stress this enough - one must *already* be aware of the declaration to write correct code. Implicit conversions can cause trouble, as well as in, out, inout, const, and all the other storage classes discussed.True enough. But I consider this a somewhat different class of unpredictable behavior, if only because it isn't something present in similar to this with a leading => at the call point).Previous discussions here frequently revolved around the need to put information in the function declaration so people have some guarantees about what the function does - const is a prime example. Another is the suggestion to move the contracts from the implementation to the declaration. There is no way to know what: somefunc(++i); does without looking at the declaration for somefunc() - even without lazy evaluation.But the possibilities are rather limited, either i will be mutated or it will not. I suppose what worries me isn't that I need to look at the declaration to determine what will happen with implicit delegate conversion, but that even looking at the declaration won't tell me what may happen. By the same token, I actually like C's method of pass-by-reference because call-side code inspection reveals which parameters may be modified. I think this is why I like the suggestion to retain curly braces and perhaps derive the return value automatically--it makes the contract between caller and callee explicit. But this is a new feature and I do want to spend more time with it before making a final decision. If nothing else, I agree that time and experience will reduce the chance of mistakes demonstrated recently, as this becomes "just another tool" we have available. Sean
Aug 22 2006
Sean Kelly wrote:Walter Bright wrote:Tom's suggestion is to introduce a different style of delegate for this lambda expression thingy. That would resolve the difficulties here (assuming delegates can be auto-converted to the lambda expr; per Tom's suggestion). But for delegate itself, it sure would be nice to have: as a shorthand forSean Kelly wrote:C++ templates are well-intentioned and really quite powerful, but they're a horrible hack, even when used as originally intended. The need for "template" as a means to distinguish dependent type names is a perfect example of something that should be completely unnecessary.I'll admit that doing so makes a difference to me, as it allows for tricks that aren't possible with explicit delegate signifiers. But I'm undecided as to whether this is a "good thing" or not--I plan to give it some time before I decide.Good - I don't think any of us are prescient enough to see all the ramifications of this. Nobody predicted where C++ templates wound up (the good and the bad).In light of my opening paragraph, I really shoudn't have said "all."And I'd like to note that all the tricks I'm thinking of for the new syntax tend to involve refactoring existing code (which is potentially dangerous, given the issue with side-effects) rather than writing new code. With new code I don't see much benefit to omitting all evidence that a parameter will be converted to a delegate, as it's something the user must be aware of to write correct code.I can't stress this enough - one must *already* be aware of the declaration to write correct code. Implicit conversions can cause trouble, as well as in, out, inout, const, and all the other storage classes discussed.True enough. But I consider this a somewhat different class of unpredictable behavior, if only because it isn't something present in similar to this with a leading => at the call point).Previous discussions here frequently revolved around the need to put information in the function declaration so people have some guarantees about what the function does - const is a prime example. Another is the suggestion to move the contracts from the implementation to the declaration. There is no way to know what: somefunc(++i); does without looking at the declaration for somefunc() - even without lazy evaluation.But the possibilities are rather limited, either i will be mutated or it will not. I suppose what worries me isn't that I need to look at the declaration to determine what will happen with implicit delegate conversion, but that even looking at the declaration won't tell me what may happen. By the same token, I actually like C's method of pass-by-reference because call-side code inspection reveals which parameters may be modified. I think this is why I like the suggestion to retain curly braces and perhaps derive the return value automatically--it makes the contract between caller and callee explicit. But this is a new feature and I do want to spend more time with it before making a final decision. If nothing else, I agree that time and experience will reduce the chance of mistakes demonstrated recently, as this becomes "just another tool" we have available. Sean
Aug 22 2006
Sean Kelly wrote:Walter Bright wrote:Interesting. I have been doing some of this myself too, but my available time has been limited. I have also been looking at the concept of iterators. I agree that foreach is the preferable solution, but in some cases, only supporting foreach is too limited. For example, only using foreach, you can not iterate over two collections simultaneously. It would be great if we could come up with a standardized Iterator concept for D that supported both foreach style iteration (opApply) and some kind of single step iteration (maybe Java-style next/hasNext). Here is a sample of how it could look (but this particular sample seems to give a segfault on DMD 0.165 for some reason): template opApplyMixin() { int opApply(int delegate(inout ValueType v) dg) { ValueType *t; while(hasNext()) { t = next(); if (auto status = dg(*t)) return status; } return 0; } } /// Simple array iterator wrapper struct ArrayIterator(T) { T[] arr; alias T ValueType; T* next() { T* r = arr.ptr; arr = arr[1..$]; return r; } bool hasNext() { return arr.length > 0; } mixin opApplyMixin; } I have been looking at implementing many of my functional algorithms as array views/iterators. For example: foreach(word; read!(char)("file.txt").splitIterate(&isSpace)) { writefln("%s",word); } where splitIterate is implemented just as the regular split, but instead of allocating and returning char[][] array, it is implemented as an iterator returning the next char[] slice for each iteration without allocating any extra data. read could probably be implemented in a smart way, reading the file a certain chunk at a time. Never using more than a constant amount of memory. Other functions as iterators I have been implementing are: split(array,predicate|substring|element) select(array,predicate|element) reverse(array) range(start,stop,next) generate(init,next) map(array,func) indexmap(array,indexarray|nextindexfunc) and so on...I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen.I've been working on a predicate-oriented algorithm module on and off for the past few weeks. A few looping constructs have been left out because foreach seems a preferable solution in D, but the rest is coming along nicely.About the only problem I had was inlining delegates passed to the functions, so the default operations are passed as structs with opCall defined instead.I've encountered this too. Delegates passed to functions are often called in tight inner loops. So far, I've never seen DMD doing a good job at inlining those cases. Structs with opCall is an excellent idea, that I will steal right away. :) It would be interesting to know if we in the future can count on const delegates being inlined just as efficiently. /Oskar
Aug 25 2006
Oskar Linde wrote:Sean Kelly wrote:I agree. And given D's somewhat limited overloading mechanism, there may simply be no point in trying to support pointers as iterators (ie. to use traits templates). This suggests that some sort of self-contained design may be best, but I haven't thought about the issue enough to comment on the details.Walter Bright wrote:Interesting. I have been doing some of this myself too, but my available time has been limited. I have also been looking at the concept of iterators. I agree that foreach is the preferable solution, but in some cases, only supporting foreach is too limited. For example, only using foreach, you can not iterate over two collections simultaneously. It would be great if we could come up with a standardized Iterator concept for D that supported both foreach style iteration (opApply) and some kind of single step iteration (maybe Java-style next/hasNext).I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen.I've been working on a predicate-oriented algorithm module on and off for the past few weeks. A few looping constructs have been left out because foreach seems a preferable solution in D, but the rest is coming along nicely.Here is a sample of how it could look (but this particular sample seems to give a segfault on DMD 0.165 for some reason): template opApplyMixin() { int opApply(int delegate(inout ValueType v) dg) { ValueType *t; while(hasNext()) { t = next(); if (auto status = dg(*t)) return status; } return 0; } } /// Simple array iterator wrapper struct ArrayIterator(T) { T[] arr; alias T ValueType; T* next() { T* r = arr.ptr; arr = arr[1..$]; return r; } bool hasNext() { return arr.length > 0; } mixin opApplyMixin; }So this would cover unidirectional iterators (forward and reverse) but not bidirectional iterators? Perhaps we need a hasPrev as well? And then there's random access iterators...I have been looking at implementing many of my functional algorithms as array views/iterators. For example: foreach(word; read!(char)("file.txt").splitIterate(&isSpace)) { writefln("%s",word); } where splitIterate is implemented just as the regular split, but instead of allocating and returning char[][] array, it is implemented as an iterator returning the next char[] slice for each iteration without allocating any extra data.I think Matthew played with this idea a bit in DTL, but I don't think he got quite this far. It's a great idea however. I can't recall if he had a name for it, but "view" seems to fit.read could probably be implemented in a smart way, reading the file a certain chunk at a time. Never using more than a constant amount of memory. Other functions as iterators I have been implementing are: split(array,predicate|substring|element) select(array,predicate|element) reverse(array) range(start,stop,next) generate(init,next) map(array,func) indexmap(array,indexarray|nextindexfunc) and so on...That's a solid proof of concept :-) Sean
Aug 31 2006
Sean Kelly wrote:Oskar Linde wrote:I still think a good move would be to allow delegates as foreach aggregates. Then a class could expose "iterators" in the form of methods, and algorithms could be represented as member delegates. -- Chris Nicholson-SaulsSean Kelly wrote:I agree. And given D's somewhat limited overloading mechanism, there may simply be no point in trying to support pointers as iterators (ie. to use traits templates). This suggests that some sort of self-contained design may be best, but I haven't thought about the issue enough to comment on the details.Walter Bright wrote:Interesting. I have been doing some of this myself too, but my available time has been limited. I have also been looking at the concept of iterators. I agree that foreach is the preferable solution, but in some cases, only supporting foreach is too limited. For example, only using foreach, you can not iterate over two collections simultaneously. It would be great if we could come up with a standardized Iterator concept for D that supported both foreach style iteration (opApply) and some kind of single step iteration (maybe Java-style next/hasNext).I've struggled to get people to accept the {} version ever since D adopted anonymous delegates. Haven't made much headway in getting such used or in it having any sort of significant impact. How many have made a "dotimes(n, exp)" function or any sort of syntax extension using it? None that I've seen.I've been working on a predicate-oriented algorithm module on and off for the past few weeks. A few looping constructs have been left out because foreach seems a preferable solution in D, but the rest is coming along nicely.Here is a sample of how it could look (but this particular sample seems to give a segfault on DMD 0.165 for some reason): template opApplyMixin() { int opApply(int delegate(inout ValueType v) dg) { ValueType *t; while(hasNext()) { t = next(); if (auto status = dg(*t)) return status; } return 0; } } /// Simple array iterator wrapper struct ArrayIterator(T) { T[] arr; alias T ValueType; T* next() { T* r = arr.ptr; arr = arr[1..$]; return r; } bool hasNext() { return arr.length > 0; } mixin opApplyMixin; }So this would cover unidirectional iterators (forward and reverse) but not bidirectional iterators? Perhaps we need a hasPrev as well? And then there's random access iterators...I have been looking at implementing many of my functional algorithms as array views/iterators. For example: foreach(word; read!(char)("file.txt").splitIterate(&isSpace)) { writefln("%s",word); } where splitIterate is implemented just as the regular split, but instead of allocating and returning char[][] array, it is implemented as an iterator returning the next char[] slice for each iteration without allocating any extra data.I think Matthew played with this idea a bit in DTL, but I don't think he got quite this far. It's a great idea however. I can't recall if he had a name for it, but "view" seems to fit.read could probably be implemented in a smart way, reading the file a certain chunk at a time. Never using more than a constant amount of memory. Other functions as iterators I have been implementing are: split(array,predicate|substring|element) select(array,predicate|element) reverse(array) range(start,stop,next) generate(init,next) map(array,func) indexmap(array,indexarray|nextindexfunc) and so on...That's a solid proof of concept :-) Sean
Aug 31 2006
Walter Bright wrote:kris wrote:Can't do that; the char[] version have to stay.So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc The D version of a logger has five 'levels' of logging: log.trace (char[]) log.info (char[]) log.warn (char[]) log.error (char[]) log.fatal (char[]) and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction: log.trace (char[] delegate() dg) log.info (char[] delegate() dg) log.warn (char[] delegate() dg) log.error (char[] delegate() dg) log.fatal (char[] delegate() dg) The support code was installed fairly recently, and the above delegate-wrappers were on the todo list. However, dmd.165 will presumeably not compile this code?Correct.Won't there be an overload ambiguity?Yes.If so, it illustrates a serious shortcoming in the new syntaxJust get rid of the (char[]) versions.
Aug 22 2006
kris wrote:Walter Bright wrote:This may not be a very clean solution, but you could make them templates, using ifti. (If that only worked for member functions...) trace(T)(T str) {...} Then trace("string"); will not be called lazily, while trace({return "string";}); will. /Oskarkris wrote:Can't do that; the char[] version have to stay.So, we have some existing code for logging. Very efficient, and highly flexible. It follows the design patterns popularized by Log4J, with Appenders, Layouts, etc The D version of a logger has five 'levels' of logging: log.trace (char[]) log.info (char[]) log.warn (char[]) log.error (char[]) log.fatal (char[]) and the intent (with dmd.164) was to add these flavours, specifically to address the point Walter makes about unnecessary message construction: log.trace (char[] delegate() dg) log.info (char[] delegate() dg) log.warn (char[] delegate() dg) log.error (char[] delegate() dg) log.fatal (char[] delegate() dg) The support code was installed fairly recently, and the above delegate-wrappers were on the todo list. However, dmd.165 will presumeably not compile this code?Correct.Won't there be an overload ambiguity?Yes.If so, it illustrates a serious shortcoming in the new syntaxJust get rid of the (char[]) versions.
Aug 22 2006
kris wrote:Can't do that; the char[] version have to stay.Why?
Aug 22 2006
Walter Bright wrote:kris wrote:Because my Boss says soCan't do that; the char[] version have to stay.Why?
Aug 22 2006
kris wrote:Walter Bright wrote:kris, really, is it a joke? If not, and I'm supposing you are using the mango logging package in some big D application, can you post us the performance difference you have noticed using the char[] version versus the delegate version? --- Paolo Invernizzikris wrote:Because my Boss says soCan't do that; the char[] version have to stay.Why?
Aug 23 2006
On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> wrote:kris wrote:I think he was being sarcastic. -JJRWalter Bright wrote:kris, really, is it a joke? If not, and I'm supposing you are using the mango logging package in some big D application, can you post us the performance difference you have noticed using the char[] version versus the delegate version? --- Paolo Invernizzikris wrote:Because my Boss says soCan't do that; the char[] version have to stay.Why?
Aug 23 2006
John Reimer wrote:On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> wrote:No sarcasm required. The recent development of deliberately introducing ambiguity into the language warrants serious concern. It's something that should, and does, trouble most of us quite deeply (judging by the responses). As for performance differences, I suspect you're missing the bigger picture, Paolo? I'll try to draw one: you may have noticed my concern about delegates and heap-based frames? If so, you'll probably have seen a reponse from Walter noting that specific types of expr can probably be identified as not escaping the hosting function? There's a gaping hole in that marketing-style claim, and you should treat it with a heathly dose of suspicion. For example; I'd like to use delegates as a nice clean shortcut for logging. Instead of writing I'd instead like to write this Makes sense, right? We don't want to invoke the cost of the sprint() function unless tracing is enabled. Note that these are guaranteed to be synchronous callbacks. The delegates are not "saved for later" like a gui delegate might be. Thus, the frame of the hosting function should be stack-based. However, because the callback references local variables, there's no guarantee the host-frame will not be allocated on the heap instead. This is the issue we'd been trying to address in another thread. Namely, if there's going to be ambiguity over whether a delegate is invoked syncrhonously or asynchronously, we probably need a way to tell the compiler what's what (rather than let it make the wrong choice). In simplistic terms, if I have a function like so (note that there's no delegate intended here): It's quite possible that the frame for this will be heap-allocated, even though the logger invocation is not intending to use delegates at all. The damn compiler might go and turn the char[] expr into a sneaky delegate, and then decide it needs a heap-frame instead of a stack one. That, my friend, is a serious performance issue (heap allocation each and every time somefunk() is called), and it's all hidden behind a plush blue-velvet curtain. Supposing we actually *want* to use a delegate in this situation? e.g. We still need some manner in which to tell the compiler that the callback is synchronous, rather than asynchronous, and that it should not be trying to allocate a heap-frame for this function. Again, this is what the other thread was discussing in depth. One of the ideas put-forward there was to somehow mark the hosting function as being a synchronous-host; something like this Another opinion was to mark the /usage/ of the delegate itself, the logger in this case, with something akin to the following Each of these have their relative pros and cons. What *is* evident, is that the compiler cannot be trusted to make the appropriate decision. In contrast, it's quite likely to make a highly conservative, and very poor, decision. Implied behaviour is often entirely the wrong thing to do and, without a means for manual control, will quickly become a most serious problem. I hope you can see where the recent ambiguities lead to? You can easily wind up with unexpected (and unwanted) delegates, which may well lead to unexpected (and unwanted) heap-based frames. To resolve, the ambiguities must be removed. Additionally, some *dependable* mechanism should be put in place to manage (or override) how & when heap-based frames are allocated.kris wrote:I think he was being sarcastic. -JJRWalter Bright wrote:kris, really, is it a joke? If not, and I'm supposing you are using the mango logging package in some big D application, can you post us the performance difference you have noticed using the char[] version versus the delegate version? --- Paolo Invernizzikris wrote:Because my Boss says soCan't do that; the char[] version have to stay.Why?
Aug 23 2006
kris wrote:I've followed the discussion. While I agree that ambiguities must be avoided if possible, and that compiler-made decision are sometimes frustrating (but we all are happy about class-all-virtual-methods!), I was just curious of the performance impact of such a change... --- Paolo InvernizziOn Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> wrote: can you post us the performance difference you have noticed using the char[] version versus the delegate version?As for performance differences, I suspect you're missing the bigger picture, Paolo?
Aug 24 2006
Paolo Invernizzi wrote:kris wrote:That's simple ~ time a trivial function by calling it several million times. Now add a malloc(40) or thereabouts to the same function and time it again. There's your performance hit. Note that multithreaded apps will run afoul of mutex-contention also. As for virtual methods; you can choose which are virtual and which are not. Seriously though, virtual methods do not allocate from the heap when they are called. Imagine if they did ...I've followed the discussion. While I agree that ambiguities must be avoided if possible, and that compiler-made decision are sometimes frustrating (but we all are happy about class-all-virtual-methods!), I was just curious of the performance impact of such a change...On Wed, 23 Aug 2006 00:33:08 -0700, Paolo Invernizzi <arathorn NOSPAM_fastwebnet.it> wrote: can you post us the performance difference you have noticed using the char[] version versus the delegate version?As for performance differences, I suspect you're missing the bigger picture, Paolo?
Aug 24 2006
Walter Bright wrote:kris wrote:Further, I had to remove the very useful delegate() overload that was added using dmd164, since dmd165 just had a fit about ambiguous arguments. So much for backward compatability :/ I don't know where you get your claim about people not willing to use "{}" but, judging by the response around here, it has no truth behind it whatsoever. Perhaps you'll honour us with some background on that? I remember when you asked folks here if it would be ok to change the cast() syntax way back when, to remove that minor ambiguity. Now you're using a suspect and pithy "{}" assertion to deliberately break what is otherwise an unambiguous syntax. And you don't ask or care whether anyone would actually mind the ensuing chaos. Whatever you've been smoking, Walter, I'd like to try some of it ~ please pass it around Cheers;Can't do that; the char[] version have to stay.Why?
Aug 22 2006
Walter Bright wrote:3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.Really? Are you saying that if the receiving function is short enough the function and its delegate use may be inlined in the calling code? I can't imagine that it would simply be inlined in the receiving function and duplicates of that wold be generated. Or at least, DMD doesn't appear to do that now. Sean
Aug 22 2006
Sean Kelly wrote:Walter Bright wrote:It doesn't do it now, but it is possible to do. What an advanced compiler can do is duplicate the function into two, one gets the value arguments that have no computation, the other gets the side effect arguments and ones that involve computation as delegates.3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.Really? Are you saying that if the receiving function is short enough the function and its delegate use may be inlined in the calling code? I can't imagine that it would simply be inlined in the receiving function and duplicates of that wold be generated. Or at least, DMD doesn't appear to do that now.
Aug 22 2006
Walter Bright wrote:Sean Kelly wrote:a.k.a foo(char[] delegate()) gets converted by the compiler into foo(char[] delegate()) foo(char[]) ??? That would be REALLY cool. But I'm not sure I trust the compiler that much.Walter Bright wrote:It doesn't do it now, but it is possible to do. What an advanced compiler can do is duplicate the function into two, one gets the value arguments that have no computation, the other gets the side effect arguments and ones that involve computation as delegates.3) It is possible that the delegate can be inlined, thus eliminating any extra overhead.Really? Are you saying that if the receiving function is short enough the function and its delegate use may be inlined in the calling code? I can't imagine that it would simply be inlined in the receiving function and duplicates of that wold be generated. Or at least, DMD doesn't appear to do that now.
Aug 23 2006
Well, if I saw: auto new_p = coalesce(p, last_p, new P()) I would not assume that new P() would be evaluated, unless p and last_p were null. The same goes for a lot of macro function usage seen with the C preprocessor. People are already dealing with this problem. A lot. I like this new feature, and would hate having to type the curlies. I feel confident I can correctly document my functions and name them such that no one would be confused. I won't, however, be using this feature with other function. Or half, or even 15%. I'll use it when it makes sense. In my opinion it is a good feature. And it makes D stand out. -[Unknown]I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once? There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt ); It would be really important for me to have readable code. I want to look at the code, and want to have an impression what will happen. What really would help, is if the the delegate is marked in some way as such. In the last releases of DMD the {} syntax was there. With it you needed the return statement. Perhaps we can choose the {} syntax with an optional return statement.... { "abc" } => char[] delegate() { return "abc"; } => char[] delegate() func( "abc" ) calls func( char[] a ) func({ "abc" }) calls func( char[] delegate() dg ) func({ return "abc"; }) calls func( char[] delegate() dg ) With that syntax one can immidiatly see "this is a delegate, if it is called or not or more than once depends on func()" and the more typing of {} is not too much. Frank
Aug 21 2006
Unknown W. Brackets wrote:Well, if I saw: auto new_p = coalesce(p, last_p, new P()) I would not assume that new P() would be evaluated, unless p and last_p were null. The same goes for a lot of macro function usage seen with the C preprocessor. People are already dealing with this problem. A lot. I like this new feature, and would hate having to type the curlies. I feel confident I can correctly document my functions and name them such that no one would be confused.I feel confident I can document all sorts of bazaar things in my own code. I also feel confident that if they are allowed/promoted then other people will use them and *NOT* document them. As to others using your/my code, you can put a page and a half of documentation in warning about side effects and it wont do one wit of good if the other guy doesn't read it. And there is no way to ensure that it will get read. I can just see the buzz now: "Did you hear about the bonehead over in XYZ department that messed up the billing program? He didn't read all of the comments in the new library and ended up costing the cooperation $n million dollars because of this "cool" feature in the D programming language. I'll never use a D lib in /my/ work."
Aug 22 2006
I feel confident I can document all sorts of bazaar things in my own code. I also feel confident that if they are allowed/promoted then other people will use them and *NOT* document them. As to others using your/my code, you can put a page and a half of documentation in warning about side effects and it wont do one wit of good if the other guy doesn't read it. And there is no way to ensure that it will get read. I can just see the buzz now: "Did you hear about the bonehead over in XYZ department that messed up the billing program? He didn't read all of the comments in the new library and ended up costing the cooperation $n million dollars because of this "cool" feature in the D programming language. I'll never use a D lib in /my/ work."Well said. Go for non-implicit delegates.
Aug 22 2006
BCS wrote:I feel confident I can document all sorts of bazaar things in my own code. I also feel confident that if they are allowed/promoted then other people will use them and *NOT* document them. As to others using your/my code, you can put a page and a half of documentation in warning about side effects and it wont do one wit of good if the other guy doesn't read it. And there is no way to ensure that it will get read. I can just see the buzz now: "Did you hear about the bonehead over in XYZ department that messed up the billing program? He didn't read all of the comments in the new library and ended up costing the cooperation $n million dollars because of this "cool" feature in the D programming language. I'll never use a D lib in /my/ work."I agree with you that comments are always wrong, out of date, or missing. With the delegate feature, one doesn't need to document it, as it is there in the declaration of the function. The fact that the delegate parameter exists is a pretty good sign that the function isn't intended to evaluate the argument exactly once, so it is worth trying to figure out what the function is doing before calling it with parameters that have side effects. Sometimes, corporations have coding standards that ban certain constructs - like using templates in C++. If a particular feature does turn out to cause grief, it can be so banned.
Aug 22 2006
Frank Benoit wrote:I think the lazy eval is a great feature, but in this form it has also great drawbacks. The code isn't that much readable as it was before. You don't know what will happen. Will that expression be evaluated or not? Or will it be evaluated more than once? There is no possibility to choose between func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );If that's the case, then something's wrong. If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.It would be really important for me to have readable code. I want to look at the code, and want to have an impression what will happen. What really would help, is if the the delegate is marked in some way as such. In the last releases of DMD the {} syntax was there. With it you needed the return statement. Perhaps we can choose the {} syntax with an optional return statement.... { "abc" } => char[] delegate()<snip> That syntax looks confusingly like an array initialiser.... Stewart.
Aug 23 2006
This is the first example, showing a ambiguity func( char[] a ) vs. func( char[] delegate() dg ) This is a second example, which has no relation to the first. func( funcRetInt() ); vs. func( &funcRetInt ); And the "vs." is not a object reference. Sorry.func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );If that's the case, then something's wrong. If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.the '=>' was not a syntax proposal "{ "abc" }" /is of type/ "char[] delegate()"{ "abc" } => char[] delegate()That syntax looks confusingly like an array initialiser....
Aug 23 2006
Frank Benoit wrote:If one overload matches exactly, it goes without saying that that's the one that's called. As such, the programmer would do func("qwert"); to call the first, and func({ return "qwert"; }); to call the second. Just like in any other case.This is the first example, showing a ambiguity func( char[] a ) vs. func( char[] delegate() dg )func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );If that's the case, then something's wrong. If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.This is a second example, which has no relation to the first. func( funcRetInt() ); vs. func( &funcRetInt );In that case, I don't get what your problem is.And the "vs." is not a object reference. Sorry.Pardon?The '=>' doesn't look anything like an array initialiser either. Unless there's some other kind of array initialiser that I haven't discovered yet.... Wasn't it obvious that I was talking about the bit to the left of those two characters? -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.the '=>' was not a syntax proposal "{ "abc" }" /is of type/ "char[] delegate()"{ "abc" } => char[] delegate()That syntax looks confusingly like an array initialiser....
Aug 23 2006
Stewart Gordon wrote:Frank Benoit wrote:That's what I would have thought as well, but its already been said that these cases are considered ambiguous... For example, the following code: Produces this error:If one overload matches exactly, it goes without saying that that's the one that's called. As such, the programmer would do func("qwert"); to call the first, and func({ return "qwert"; }); to call the second. Just like in any other case.This is the first example, showing a ambiguity func( char[] a ) vs. func( char[] delegate() dg )func( char[] a ) vs. func( char[] delegate() dg ) func( funcRetInt() ); vs. func( &funcRetInt );If that's the case, then something's wrong. If funcRetInt returns a char[], despite the name, then funcRetInt() is of type char[]. Therefore it should match this first.The only thing I can think of is the PHP format: As far as I know, the D way is to use a colon (:) for this sort of thing. Although the bit on the left doesn't look like an array initialiser either, in terms of D, as they use brackets rather than braces. Static structure literals do use braces, however, so there's still some ambiguity. -- Chris Nicholson-SaulsThis is a second example, which has no relation to the first. func( funcRetInt() ); vs. func( &funcRetInt );In that case, I don't get what your problem is.And the "vs." is not a object reference. Sorry.Pardon?The '=>' doesn't look anything like an array initialiser either. Unless there's some other kind of array initialiser that I haven't discovered yet.... Wasn't it obvious that I was talking about the bit to the left of those two characters?the '=>' was not a syntax proposal "{ "abc" }" /is of type/ "char[] delegate()"{ "abc" } => char[] delegate()That syntax looks confusingly like an array initialiser....
Aug 23 2006
Chris Nicholson-Sauls wrote:Stewart Gordon wrote:<snip>Frank Benoit wrote:Said by whom, where exactly? Moreover: http://www.digitalmars.com/d/function.html "In D, function overloading is simple. It matches exactly, it matches with implicit conversions, or it does not match. If there is more than one match, it is an error." The irony is that this seems to be a case of what this sentence says, rather than what it was actually supposed to mean. <snip>That's what I would have thought as well, but its already been said that these cases are considered ambiguous... For example, the following code:This is the first example, showing a ambiguity func( char[] a ) vs. func( char[] delegate() dg )If one overload matches exactly, it goes without saying that that's the one that's called. As such, the programmer would do func("qwert"); to call the first, and func({ return "qwert"; }); to call the second. Just like in any other case.Well I don't speak PHP, so no wonder I didn't think of that.The '=>' doesn't look anything like an array initialiser either. Unless there's some other kind of array initialiser that I haven't discovered yet.... Wasn't it obvious that I was talking about the bit to the left of those two characters?The only thing I can think of is the PHP format:As far as I know, the D way is to use a colon (:) for this sort of thing. Although the bit on the left doesn't look like an array initialiser either, in terms of D, as they use brackets rather than braces.<snip> Of course! I'm normally more fluent in D than to confuse these notations. So a struct initialiser is what it looks confusingly like. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Aug 25 2006
Stewart Gordon wrote:Chris Nicholson-Sauls wrote:I know I've read it, don't recall exactly by whom or suchlike. However, it speaks for itself, try it with DMD 0.165 and behold the error. Mind you I'm in agreement that it shouldn't be that way: thus I feel the lazy evaluation feature, while nifty and full of Trendy Cooltasticism (TM), is essentially b0rked in DMD165. I do look forward to the next version and the 'lazy' parameter class. -- Chris Nicholson-SaulsStewart Gordon wrote:<snip>Frank Benoit wrote:Said by whom, where exactly? Moreover: http://www.digitalmars.com/d/function.html "In D, function overloading is simple. It matches exactly, it matches with implicit conversions, or it does not match. If there is more than one match, it is an error." The irony is that this seems to be a case of what this sentence says, rather than what it was actually supposed to mean. <snip>That's what I would have thought as well, but its already been said that these cases are considered ambiguous... For example, the following code:This is the first example, showing a ambiguity func( char[] a ) vs. func( char[] delegate() dg )If one overload matches exactly, it goes without saying that that's the one that's called. As such, the programmer would do func("qwert"); to call the first, and func({ return "qwert"; }); to call the second. Just like in any other case.
Aug 25 2006