August 22, 2006 Prefer { } syntax? - was Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | On Mon, 21 Aug 2006 22:41:24 -0400, Walter Bright <newshound@digitalmars.com> wrote: > Derek Parnell wrote: >> On Mon, 21 Aug 2006 16:33:00 -0700, Walter Bright wrote: >>> 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 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. 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. |
August 22, 2006 Re: Lazy eval -- an example issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote:
> 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 syntax
>
>
> 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. Why don't you conduct a poll asking exactly which 'programmers' won't use the {} delimeters to unambiguously declare a delegate?
|
August 22, 2006 Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to kris | kris wrote: >> 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. 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. |
August 22, 2006 Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frank Benoit | 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
>
|
August 22, 2006 Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | 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 said > Suppose 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 |
August 22, 2006 Re: Lazy eval -- an example issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to kris | kris wrote: > Walter Bright wrote: >> 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. 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. > 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. |
August 22, 2006 Re: Prefer { } syntax? - was Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Chris Miller | 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. |
August 22, 2006 Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Derek Parnell | 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. >>> Let's assume the original API said >> Suppose 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. I agree. |
August 22, 2006 Re: Lazy eval | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote: > 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. 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. >> 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. 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. /Oskar |
August 22, 2006 Re: Lazy eval -- an example issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to Walter Bright | Walter Bright wrote: > kris wrote: > >> Walter Bright wrote: >> >>> 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. > > > 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. > >> 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.) 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. > >> 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. How do you know this? Where exactly does your information come from, and how is it measured? > > 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. |
Copyright © 1999-2021 by the D Language Foundation