digitalmars.D - Request: Implement default arguments properly (unlike C).
- Don Clugston (62/62) Aug 03 2005 It seems that D has inherited the C/C++ semantics for default arguments....
- AJG (17/78) Aug 03 2005 Hi there,
- Don Clugston (57/70) Aug 03 2005 They're just trivial inline functions. If the optimiser can't inline the...
- AJG (18/66) Aug 03 2005 Hmm... good point. Although I am a fan of whole program static analysis ...
- Don Clugston (30/82) Aug 04 2005 Well, it would be a first step, it would close the hole in the language,...
- Don Clugston (30/82) Aug 04 2005 Well, it would be a first step, it would close the hole in the language,...
- Deewiant (18/23) Aug 04 2005 But not if they refer to other arguments, which I think is an
- AJG (24/47) Aug 04 2005 Yes, yes it would. If I may throw a wild guess about why that's not curr...
- Georg Bauhaus (4/11) Aug 04 2005 int foo(int x, int y = foo(x))
- AJG (7/15) Aug 04 2005 IIRC recursive default parameter definitions cause an out of memory faul...
- Georg Bauhaus (17/22) Aug 04 2005 The compiler is fine with the following indirect recursion,
- Ben Hinkle (6/17) Aug 04 2005 If the functions are methods in a class the optimizer can't inline them
- BCS (10/103) Aug 03 2005 I'd like to know how default parameters are actually implemented, howeve...
- Manfred Nowak (11/13) Aug 03 2005 [...]
It seems that D has inherited the C/C++ semantics for default arguments. This is an anachronism which IMHO, D should fix. THE PROBLEM WITH C DEFAULT ARGUMENTS Consider this code. int afunc(int x) { return x*2; } int main() { int z = afunc(7); // A int function(int) f; f= &afunc; // B int w = f(7); // C return w; } Obviously A and C are identical calls to afunc(). Now change the declaration of Afunc to int afunc(int x, int y=3) Line (A) still works. But line (B) won't compile. Why not? In line (A), it still looks as though there's a one-parameter function called afunc(). But it's a fraud. afunc() demands two parameters. The second parameter is what Herb Sutter calls a "peek-a-boo" parameter: it's hidden most of the time, but sometimes it pops out unexpectedly and surprises you. We can change the declaration of f to make B compile, but there's no way to change line (C) to say 'use the default argument'. The problem also applies to delegates as well as to function pointers. If you add a default parameter to an existing function, one effect is to break all existing code which delegates to that function. This is, in my opinion, completely absurd. But a change to the meaning of default arguments would completely solve the problem. PROPOSAL The solution is really very simple. int afunc(int x, int y=3) { ..dosomething } should be *identical* to declaring TWO functions: int afunc(int x) { afunc(x, 3); } int afunc(int x, int y) { .. dosomething } The optimiser can recognise that a call to afunc(7) can be inlined to afunc(7,3), so it would likely generate exactly the same code as before. (Side note: if done in this way, there's actually no reason why the default parameters have to be the last ones. The parser could just check if a function was being redeclared). All references to default arguments could be stripped out of the parser, except at the point where the declaration is parsed. Everywhere else, it just behaves as an overloaded function. I think this could potentially simplify the language. I don't know if this would work for templates as well. Since D has template typedefs, perhaps it would. But AFAIK template peek-a-boo arguments aren't the same problem that function parameters are. I think that the only reason that C default parameters work the way they do is because when they were introduced, overloading didn't exist. Now that we have real overloading, the bugs and complexity associated with this anachronistic "poor mans overloading" can be abolished. How about it?
Aug 03 2005
Hi there, I am inclined to agree with you regarding the problem (limitation) you mentioned in line "C." At the same time, I'm not so fond of your solution for 2 reasons: 1) Performance: Adding extra function calls would slow things down. 2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right? Having said that, once again I agree that D's default parameters are far from perfect. While we are discussing them, I'll throw in my own complaints (I hope you don't mind): A) When calling, you can't skip any defaults (Very annoying). B) Within a class, you can't use member vars as defaults (Ditto). Cheers, --AJG. PS: AFAIK, unlike C++, C doesn't have default parameters. In article <dcro7a$235c$1 digitaldaemon.com>, Don Clugston says...It seems that D has inherited the C/C++ semantics for default arguments. This is an anachronism which IMHO, D should fix. THE PROBLEM WITH C DEFAULT ARGUMENTS Consider this code. int afunc(int x) { return x*2; } int main() { int z = afunc(7); // A int function(int) f; f= &afunc; // B int w = f(7); // C return w; } Obviously A and C are identical calls to afunc(). Now change the declaration of Afunc to int afunc(int x, int y=3) Line (A) still works. But line (B) won't compile. Why not? In line (A), it still looks as though there's a one-parameter function called afunc(). But it's a fraud. afunc() demands two parameters. The second parameter is what Herb Sutter calls a "peek-a-boo" parameter: it's hidden most of the time, but sometimes it pops out unexpectedly and surprises you. We can change the declaration of f to make B compile, but there's no way to change line (C) to say 'use the default argument'. The problem also applies to delegates as well as to function pointers. If you add a default parameter to an existing function, one effect is to break all existing code which delegates to that function. This is, in my opinion, completely absurd. But a change to the meaning of default arguments would completely solve the problem. PROPOSAL The solution is really very simple. int afunc(int x, int y=3) { ..dosomething } should be *identical* to declaring TWO functions: int afunc(int x) { afunc(x, 3); } int afunc(int x, int y) { .. dosomething } The optimiser can recognise that a call to afunc(7) can be inlined to afunc(7,3), so it would likely generate exactly the same code as before. (Side note: if done in this way, there's actually no reason why the default parameters have to be the last ones. The parser could just check if a function was being redeclared). All references to default arguments could be stripped out of the parser, except at the point where the declaration is parsed. Everywhere else, it just behaves as an overloaded function. I think this could potentially simplify the language. I don't know if this would work for templates as well. Since D has template typedefs, perhaps it would. But AFAIK template peek-a-boo arguments aren't the same problem that function parameters are. I think that the only reason that C default parameters work the way they do is because when they were introduced, overloading didn't exist. Now that we have real overloading, the bugs and complexity associated with this anachronistic "poor mans overloading" can be abolished.
Aug 03 2005
I am inclined to agree with you regarding the problem (limitation) you mentioned in line "C." At the same time, I'm not so fond of your solution for 2 reasons: 1) Performance: Adding extra function calls would slow things down.They're just trivial inline functions. If the optimiser can't inline them, it is an extremely poor optimiser. It's one of the most trivial optimisations you can make. But note that it also gives an opportunity for the compiler to perform additional optimisation. Suppose that *every* use of the function func(a, b=7) used the default parameter. Then, instead of inlining each invocation of the function, the optimiser could inline the whole of the general func(a, b) into func(a), turning b into the constant 7. With the existing behaviour, this optimisation would only be possible with complex whole-program optimisation. (The this-function-is-only-ever-called-from-one-place optimisation is much simpler to detect than the this-function-is-always-called-with-the-same-last-n-parameters optimisation). If the creation of the default parameters generated a lot of code for constructors etc, and the function was called from many different places, then the compiler might choose to keep it non-inline in order to reduce code size.2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right?If we retain compatibility with C++, which is what I'm proposing (ie, only the final parameters can be defaulted), then it's only a linear number of ghost functions. Eg void func(int a=1, int b=2, int c=3, int c=4) becomes: func(int a, int b, int c, int d) {} func(int a, int b, int c) { func(a, b, c, 4); } func(int a, int b) { func(a, b, 3, 4); } func(int a) { func(a, 2, 3, 4); } func() { func(1,2,3,4); } Even if default parameters could be inserted into an existing list, it is still only N ghost functions. eg func(a, b=2, c=3, d=4, e) { dostuff...} gives func(a, e) { func(a, 2, 3, 4, e); } func(a, b, e) { func(a, b, 3, 4, e); } func(a, b, c, e) { func(a, b, c, 4, e); } func(a, b, c, d, e) { dostuff... } I'm not proposing this, but it's a trivial extension. The only reason you might do this is that it might give a more natural parameter ordering. Complexity would only come if you were allowed to 'skip' parameters; then it's 2^N. Surely this would require some storage of the default parameter list. It would be difficult to maintain existing behaviour in cases where two or more default parameters.Having said that, once again I agree that D's default parameters are far from perfect. While we are discussing them, I'll throw in my own complaints (I hope you don't mind): A) When calling, you can't skip any defaults (Very annoying).Avoid the 2^N behaviour and the ambiguities would be tricky. Unless you could use , to skip that parameter. Which doesn't sit well with my proposal, because the caller has to know about the existence of default parameters.B) Within a class, you can't use member vars as defaults (Ditto).Now that's an intesting one. I think my proposal would make it much easier to do that, because when the function is created, you have full access to the class which created it. Heck, you could have a default which wasn't even constant! eg. func(int a, int b=rand()%a) {} becomes func(a, b) func(a) { func(a, rand()%a); } // uses a different value of b every time! That would be really hard to do with the existing default parameters; the value is stored as a defaultArg member of the Argument struct, and just copied in place. If you had to store an expression, that would be a mess. Thanks, I think you've just strengthened my case enormously!PS: AFAIK, unlike C++, C doesn't have default parameters.You're probably right. It's been a _very_ long time since I used C. Cheers, -Don.
Aug 03 2005
Hi,I suppose this is a valid assumption re: optimizer.I am inclined to agree with you regarding the problem (limitation) you mentioned in line "C." At the same time, I'm not so fond of your solution for 2 reasons: 1) Performance: Adding extra function calls would slow things down.They're just trivial inline functions. If the optimiser can't inline them, it is an extremely poor optimiser. It's one of the most trivial optimisations you can make.But note that it also gives an opportunity for the compiler to perform additional optimisation. Suppose that *every* use of the function func(a, b=7) used the default parameter. Then, instead of inlining each invocation of the function, the optimiser could inline the whole of the general func(a, b) into func(a), turning b into the constant 7. With the existing behaviour, this optimisation would only be possible with complex whole-program optimisation. (The this-function-is-only-ever-called-from-one-place optimisation is much simpler to detect than the this-function-is-always-called-with-the-same-last-n-parameters optimisation).Hmm... good point. Although I am a fan of whole program static analysis ;).If the creation of the default parameters generated a lot of code for constructors etc, and the function was called from many different places, then the compiler might choose to keep it non-inline in order to reduce code size.But I thought you wanted to move beyond "C++ semantics?" If we're to do this, I think we should get rid of skipping restrictions.2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right?If we retain compatibility with C++,But skipping parameters would be soooooooo nice... ;)Having said that, once again I agree that D's default parameters are far from perfect. While we are discussing them, I'll throw in my own complaints (I hope you don't mind): A) When calling, you can't skip any defaults (Very annoying).Avoid the 2^N behaviour and the ambiguities would be tricky. Unless you could use , to skip that parameter. Which doesn't sit well with my proposal, because the caller has to know about the existence of default parameters.If this is so, then it would definitely be a big step forward. The only thing missing would be skipping parameters, and we'd have awesome functionality.B) Within a class, you can't use member vars as defaults (Ditto).Now that's an intesting one. I think my proposal would make it much easier to that, because when the function is created, you have full access to the class which created it.Heck, you could have a default which wasn't even constant! eg. func(int a, int b=rand()%a) {} becomes func(a, b) func(a) { func(a, rand()%a); } // uses a different value of b every time! That would be really hard to do with the existing default parameters; the value is stored as a defaultArg member of the Argument struct, and just copied in place. If you had to store an expression, that would be a mess.Hm... I believe non-constant expressions are already allowed: int Bar() { return (0); } int Foo(int b = Bar() % 2) { return (b); } void main() { Foo(); } // Compiles fine.Thanks, I think you've just strengthened my case enormously!No problemo.<jealous> You haven't missed much. We have booleans now. </jealous> Cheers, --AJG.PS: AFAIK, unlike C++, C doesn't have default parameters.You're probably right. It's been a _very_ long time since I used C.
Aug 03 2005
Me too. But I'm less confident that I'll get it. Until someone manages to clone Walter.But note that it also gives an opportunity for the compiler to perform additional optimisation. Suppose that *every* use of the function func(a, b=7) used the default parameter. Then, instead of inlining each invocation of the function, the optimiser could inline the whole of the general func(a, b) into func(a), turning b into the constant 7. With the existing behaviour, this optimisation would only be possible with complex whole-program optimisation. (The this-function-is-only-ever-called-from-one-place optimisation is much simpler to detect than the this-function-is-always-called-with-the-same-last-n-parameters optimisation).Hmm... good point. Although I am a fan of whole program static analysis ;).Well, it would be a first step, it would close the hole in the language, and I think it wouldn't be too much work. It's kind of a language bug fix. Overcoming the skipping restrictions is new functionality rather than a bug fix.If the creation of the default parameters generated a lot of code for constructors etc, and the function was called from many different places, then the compiler might choose to keep it non-inline in order to reduce code size.But I thought you wanted to move beyond "C++ semantics?" If we're to do this, I think we should get rid of skipping restrictions.2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right?If we retain compatibility with C++,I agree. I remember some Windows API calls (CreateWindow(), I think) where you specified CW_USEDEFAULT to skip a parameter. It was quite clunky, and I guess the function did the default value substitution itself. My reservations about this, though, are: (1) if you have a row of commas in your function call, isn't it going to be a bit hard to read? Is that four commas, or five? (2) if you're skipping default values, maybe there's a design flaw somewhere? Default values are just a shortcut way of declaring multiple ways to call the same function (syntactic sugar). If you're skipping some default values but using others, maybe the interface is wrong; maybe you should be adding an extra function, or changing the parameters to be a collection of properties, or something. I'm not certain about this, I'm just a bit uneasy that it might promote bad design. It would be good to provide a concrete example.But skipping parameters would be soooooooo nice... ;)A) When calling, you can't skip any defaults (Very annoying).Avoid the 2^N behaviour and the ambiguities would be tricky. Unless you could use , to skip that parameter. Which doesn't sit well with my proposal, because the caller has to know about the existence of default parameters.OK, it must be copying an entire expression tree. Interesting. But what scope is that expression in? It won't work in a class, because the defaultArg item doesn't store a 'this' pointer. (In my proposal, the scope would be inside the class.) I tried putting Bar and Foo into a class, and made Bar a static function, with an interesting result: it compiled OK, but crashed at runtime :) It works OK if Foo is a static function.If this is so, then it would definitely be a big step forward. The only thing missing would be skipping parameters, and we'd have awesome functionality.B) Within a class, you can't use member vars as defaults (Ditto).Now that's an intesting one. I think my proposal would make it much easier to that, because when the function is created, you have full access to the class which created it.Heck, you could have a default which wasn't even constant! eg. func(int a, int b=rand()%a) {} becomes func(a, b) func(a) { func(a, rand()%a); } // uses a different value of b every time! That would be really hard to do with the existing default parameters; the value is stored as a defaultArg member of the Argument struct, and just copied in place. If you had to store an expression, that would be a mess.Hm... I believe non-constant expressions are already allowed: int Bar() { return (0); } int Foo(int b = Bar() % 2) { return (b); } void main() { Foo(); } // Compiles fine.LOL! But there's a lot to like about C99, they don't seem to have introduced anything really stupid, unlike another language that springs to mind... Sigh...so many poor decisions have been made in the history of C++. I have a real love/hate relationship with C++. -Don<jealous> You haven't missed much. We have booleans now. </jealous>PS: AFAIK, unlike C++, C doesn't have default parameters.You're probably right. It's been a _very_ long time since I used C.
Aug 04 2005
Me too. But I'm less confident that I'll get it. Until someone manages to clone Walter.But note that it also gives an opportunity for the compiler to perform additional optimisation. Suppose that *every* use of the function func(a, b=7) used the default parameter. Then, instead of inlining each invocation of the function, the optimiser could inline the whole of the general func(a, b) into func(a), turning b into the constant 7. With the existing behaviour, this optimisation would only be possible with complex whole-program optimisation. (The this-function-is-only-ever-called-from-one-place optimisation is much simpler to detect than the this-function-is-always-called-with-the-same-last-n-parameters optimisation).Hmm... good point. Although I am a fan of whole program static analysis ;).Well, it would be a first step, it would close the hole in the language, and I think it wouldn't be too much work. It's kind of a language bug fix. Overcoming the skipping restrictions is new functionality rather than a bug fix.If the creation of the default parameters generated a lot of code for constructors etc, and the function was called from many different places, then the compiler might choose to keep it non-inline in order to reduce code size.But I thought you wanted to move beyond "C++ semantics?" If we're to do this, I think we should get rid of skipping restrictions.2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right?If we retain compatibility with C++,I agree. I remember some Windows API calls (CreateWindow(), I think) where you specified CW_USEDEFAULT to skip a parameter. It was quite clunky, and I guess the function did the default value substitution itself. My reservations about this, though, are: (1) if you have a row of commas in your function call, isn't it going to be a bit hard to read? Is that four commas, or five? (2) if you're skipping default values, maybe there's a design flaw somewhere? Default values are just a shortcut way of declaring multiple ways to call the same function (syntactic sugar). If you're skipping some default values but using others, maybe the interface is wrong; maybe you should be adding an extra function, or changing the parameters to be a collection of properties, or something. I'm not certain about this, I'm just a bit uneasy that it might promote bad design. It would be good to provide a concrete example.But skipping parameters would be soooooooo nice... ;)A) When calling, you can't skip any defaults (Very annoying).Avoid the 2^N behaviour and the ambiguities would be tricky. Unless you could use , to skip that parameter. Which doesn't sit well with my proposal, because the caller has to know about the existence of default parameters.OK, it must be copying an entire expression tree. Interesting. But what scope is that expression in? It won't work in a class, because the defaultArg item doesn't store a 'this' pointer. (In my proposal, the scope would be inside the class.) I tried putting Bar and Foo into a class, and made Bar a static function, with an interesting result: it compiled OK, but crashed at runtime :) It works OK if Foo is a static function.If this is so, then it would definitely be a big step forward. The only thing missing would be skipping parameters, and we'd have awesome functionality.B) Within a class, you can't use member vars as defaults (Ditto).Now that's an intesting one. I think my proposal would make it much easier to that, because when the function is created, you have full access to the class which created it.Heck, you could have a default which wasn't even constant! eg. func(int a, int b=rand()%a) {} becomes func(a, b) func(a) { func(a, rand()%a); } // uses a different value of b every time! That would be really hard to do with the existing default parameters; the value is stored as a defaultArg member of the Argument struct, and just copied in place. If you had to store an expression, that would be a mess.Hm... I believe non-constant expressions are already allowed: int Bar() { return (0); } int Foo(int b = Bar() % 2) { return (b); } void main() { Foo(); } // Compiles fine.LOL! But there's a lot to like about C99, they don't seem to have introduced anything really stupid, unlike another language that springs to mind... Sigh...so many poor decisions have been made in the history of C++. I have a real love/hate relationship with C++. -Don<jealous> You haven't missed much. We have booleans now. </jealous>PS: AFAIK, unlike C++, C doesn't have default parameters.You're probably right. It's been a _very_ long time since I used C.
Aug 04 2005
AJG wrote:Hm... I believe non-constant expressions are already allowed: int Bar() { return (0); } int Foo(int b = Bar() % 2) { return (b); } void main() { Foo(); } // Compiles fine.But not if they refer to other arguments, which I think is an important/very useful feature. E.g. in the following function signatures it'd be quite handy: int foo(int x, int y = x % 2) int bar(int[] a, int b = a.length) Currently neither compiles. This (along with every other problem) can be solved using overloading, but that is a _pain_. After all, default arguments should IMO be just syntactic sugar for precisely that, as in the original proposal. Inability to use members as defaults in a class, which you already fortunately I haven't personally run into that problem yet. <g> I don't really mind not being able to skip defaults, although something like writing "default" in place of a function parameter would doubtless be handy.
Aug 04 2005
Hi,AJG wrote:Yes, yes it would. If I may throw a wild guess about why that's not currently allow: It could be that argument evaluation order is not guaranteed (just like in C), therefore it might not know A before B. If that's the case, I say please get rid of this archaic restriction and once and for all guarantee left-to-right evaluation, which might then allow what you propose to be work. IIRC, the original C non-guarantee was due to some limitation of the architecture of the PDP-11, so it seems majestically anachronistic to maintain it.Hm... I believe non-constant expressions are already allowed: int Bar() { return (0); } int Foo(int b = Bar() % 2) { return (b); } void main() { Foo(); } // Compiles fine.But not if they refer to other arguments, which I think is an important/very useful feature. E.g. in the following function signatures it'd be quite handy: int foo(int x, int y = x % 2) int bar(int[] a, int b = a.length)Currently neither compiles. This (along with every other problem) can be solved using overloading, but that is a _pain_. After all, default arguments should IMO be just syntactic sugar for precisely that, as in the original proposal.I don't really care too much about the underlying implementation as long as it accomplishes the goal and it's not terribly inefficient.Inability to use members as defaults in a class, which you already fortunately I haven't personally run into that problem yet. <g>I haven't either.I don't really mind not being able to skip defaults, although something like writing "default" in place of a function parameter would doubtless be handy.By "skip" I mean anything that allows such behaviour. This could be done (syntactically) with just successive commas: Or with a "default" or "skip" keyword: Or perhaps with a symbol: Or any other similar way. Whatever's easiest to implement. I would just want the actual functionality. This too IMO would be quite handy. Cheers, --AJG.
Aug 04 2005
AJG wrote:int foo(int x, int y = foo(x)) what now? -- Georgint foo(int x, int y = x % 2) int bar(int[] a, int b = a.length)Yes, yes it would. If I may throw a wild guess about why that's not currently allow: It could be that argument evaluation order is not guaranteed (just like in C), therefore it might not know A before B.
Aug 04 2005
Hi,IIRC recursive default parameter definitions cause an out of memory fault or somesuch in the compiler - whether they use previous parameters or not. So the point is moot. E.g.: Cheers, --AJG.int foo(int x, int y = foo(x))int foo(int x, int y = x % 2) int bar(int[] a, int b = a.length)Yes, yes it would. If I may throw a wild guess about why that's not currently allow: It could be that argument evaluation order is not guaranteed (just like in C), therefore it might not know A before B.
Aug 04 2005
AJG wrote:The compiler is fine with the following indirect recursion, but the executable runs in an infinite loop until segmentation fault. Seems like the practical programmer is in charge ;-) int bar() { return foo(42); } int foo(int x, int y = bar()) { return 0; } int main() { return foo(3); }int foo(int x, int y = foo(x))IIRC recursive default parameter definitions cause an out of memory fault or somesuch in the compiler - whether they use previous parameters or not.
Aug 04 2005
"Don Clugston" <Don_member pathlink.com> wrote in message news:dcs261$2ad4$1 digitaldaemon.com...If the functions are methods in a class the optimizer can't inline them since they might be overridden in a subclass (unless the user code is simple enough that the compiler can determine the runtime type and not treat the method call as a virtual dispatch).I am inclined to agree with you regarding the problem (limitation) you mentioned in line "C." At the same time, I'm not so fond of your solution for 2 reasons: 1) Performance: Adding extra function calls would slow things down.They're just trivial inline functions. If the optimiser can't inline them, it is an extremely poor optimiser. It's one of the most trivial optimisations you can make.
Aug 04 2005
I'd like to know how default parameters are actually implemented, however one implementation that wold solve several of these issues comes to mind. Each possible function signature that uses any default parameters is implemented as a stub that just rearranges the stack and does a goto to the normal function, maybe even inside some of the intro code. This could work something like tail recursion, thus no extra function calls. As to the "lots of ghost" issue, these stubs would be tiny, ~10-20 op codes maybe a little more if function calls and such are allowed as the defaults. Besides many of these function would get written anyway because the functionality is needed. In article <dcrrog$25qs$1 digitaldaemon.com>, AJG says...1) Performance: Adding extra function calls would slow things down. 2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right? Having said that, once again I agree that D's default parameters are far from perfect. While we are discussing them, I'll throw in my own complaints (I hope you don't mind): A) When calling, you can't skip any defaults (Very annoying). B) Within a class, you can't use member vars as defaults (Ditto). Cheers, --AJG. PS: AFAIK, unlike C++, C doesn't have default parameters. In article <dcro7a$235c$1 digitaldaemon.com>, Don Clugston says...It seems that D has inherited the C/C++ semantics for default arguments. This is an anachronism which IMHO, D should fix. THE PROBLEM WITH C DEFAULT ARGUMENTS Consider this code. int afunc(int x) { return x*2; } int main() { int z = afunc(7); // A int function(int) f; f= &afunc; // B int w = f(7); // C return w; } Obviously A and C are identical calls to afunc(). Now change the declaration of Afunc to int afunc(int x, int y=3) Line (A) still works. But line (B) won't compile. Why not? In line (A), it still looks as though there's a one-parameter function called afunc(). But it's a fraud. afunc() demands two parameters. The second parameter is what Herb Sutter calls a "peek-a-boo" parameter: it's hidden most of the time, but sometimes it pops out unexpectedly and surprises you. We can change the declaration of f to make B compile, but there's no way to change line (C) to say 'use the default argument'. The problem also applies to delegates as well as to function pointers. If you add a default parameter to an existing function, one effect is to break all existing code which delegates to that function. This is, in my opinion, completely absurd. But a change to the meaning of default arguments would completely solve the problem. PROPOSAL The solution is really very simple. int afunc(int x, int y=3) { ..dosomething } should be *identical* to declaring TWO functions: int afunc(int x) { afunc(x, 3); } int afunc(int x, int y) { .. dosomething } The optimiser can recognise that a call to afunc(7) can be inlined to afunc(7,3), so it would likely generate exactly the same code as before. (Side note: if done in this way, there's actually no reason why the default parameters have to be the last ones. The parser could just check if a function was being redeclared). All references to default arguments could be stripped out of the parser, except at the point where the declaration is parsed. Everywhere else, it just behaves as an overloaded function. I think this could potentially simplify the language. I don't know if this would work for templates as well. Since D has template typedefs, perhaps it would. But AFAIK template peek-a-boo arguments aren't the same problem that function parameters are. I think that the only reason that C default parameters work the way they do is because when they were introduced, overloading didn't exist. Now that we have real overloading, the bugs and complexity associated with this anachronistic "poor mans overloading" can be abolished.
Aug 03 2005
Hi, In article <dcs3vq$2bmj$1 digitaldaemon.com>, BCS says...I'd like to know how default parameters are actually implemented, however one implementation that wold solve several of these issues comes to mind. Each possible function signature that uses any default parameters is as a stub that just rearranges the stack and does a goto to the normal maybe even inside some of the intro code.Would this allow for skipping parameters, or would it create the 2^N scenario? Or would that not matter?This could work something like tail recursion, thus no extra function calls. As to the "lots of ghost" issue, these stubs would be tiny, ~10-20 op codes maybe little more if function calls and such are allowed as the defaults. Besides of these function would get written anyway because the functionality is needed.Thinking about this a bit more, I wouldn't mind having a lot of ghosts, if it's a flat set of ghosts (meaning one extra call). What I was worried about would be if ghosts called other ghosts arbitrarily, but I guess this wouldn't happen. Thanks, --AJG.In article <dcrrog$25qs$1 digitaldaemon.com>, AJG says...1) Performance: Adding extra function calls would slow things down. 2) Complexity: What happens when you have more than one default parameter? If we follow your idea, we would get an exponential number of posibilities and therefore an equal amount of "ghost" functions that would need to be created. Right? Having said that, once again I agree that D's default parameters are far from perfect. While we are discussing them, I'll throw in my own complaints (I hope you don't mind): A) When calling, you can't skip any defaults (Very annoying). B) Within a class, you can't use member vars as defaults (Ditto). Cheers, --AJG. PS: AFAIK, unlike C++, C doesn't have default parameters. In article <dcro7a$235c$1 digitaldaemon.com>, Don Clugston says...It seems that D has inherited the C/C++ semantics for default arguments. This is an anachronism which IMHO, D should fix. THE PROBLEM WITH C DEFAULT ARGUMENTS Consider this code. int afunc(int x) { return x*2; } int main() { int z = afunc(7); // A int function(int) f; f= &afunc; // B int w = f(7); // C return w; } Obviously A and C are identical calls to afunc(). Now change the declaration of Afunc to int afunc(int x, int y=3) Line (A) still works. But line (B) won't compile. Why not? In line (A), it still looks as though there's a one-parameter function called afunc(). But it's a fraud. afunc() demands two parameters. The second parameter is what Herb Sutter calls a "peek-a-boo" parameter: it's hidden most of the time, but sometimes it pops out unexpectedly and surprises you. We can change the declaration of f to make B compile, but there's no way to change line (C) to say 'use the default argument'. The problem also applies to delegates as well as to function pointers. If you add a default parameter to an existing function, one effect is to break all existing code which delegates to that function. This is, in my opinion, completely absurd. But a change to the meaning of default arguments would completely solve the problem. PROPOSAL The solution is really very simple. int afunc(int x, int y=3) { ..dosomething } should be *identical* to declaring TWO functions: int afunc(int x) { afunc(x, 3); } int afunc(int x, int y) { .. dosomething } The optimiser can recognise that a call to afunc(7) can be inlined to afunc(7,3), so it would likely generate exactly the same code as before. (Side note: if done in this way, there's actually no reason why the default parameters have to be the last ones. The parser could just check if a function was being redeclared). All references to default arguments could be stripped out of the parser, except at the point where the declaration is parsed. Everywhere else, it just behaves as an overloaded function. I think this could potentially simplify the language. I don't know if this would work for templates as well. Since D has template typedefs, perhaps it would. But AFAIK template peek-a-boo arguments aren't the same problem that function parameters are. I think that the only reason that C default parameters work the way they do is because when they were introduced, overloading didn't exist. Now that we have real overloading, the bugs and complexity associated with this anachronistic "poor mans overloading" can be abolished.
Aug 03 2005
In article <dcs6d5$2ef3$1 digitaldaemon.com>, AJG says...Yes parameter skipping would be allowed (possible) with this scheme. The problem would be ambiguity e.g. Foo(int a, int b=1, int c=2) Foo(1, 0); // Foo(a,b) or Foo(a,c) One possibility would be allowing explicit creation of stubs e.g. Foo(int a, int b) { return a+b; } stub(int a) { b=0; } stub(real r) { a=cast(int)r; b=cast(int)r*2; } or something like thatEach possible function signature that uses any default parameters is as a stub that just rearranges the stack and does a goto to the normal maybe even inside some of the intro code.Would this allow for skipping parameters, or would it create the 2^N scenario? Or would that not matter?
Aug 04 2005
Hi,Hm... I see no ambiguity here. Foo(1, 0) can only be Foo(a, b). To get Foo(a, c) you'd have to do: Foo(1, , 0); or Foo(1, default, 0); or Foo(1, ?, 0); Or whatever syntax is eventually selected. But the key is that there should be a placeholder. That alone removes the ambiguities. Cheers, --AJG.Yes parameter skipping would be allowed (possible) with this scheme. The problem would be ambiguity e.g. Foo(int a, int b=1, int c=2) Foo(1, 0); // Foo(a,b) or Foo(a,c)Each possible function signature that uses any default parameters is as a stub that just rearranges the stack and does a goto to the normal maybe even inside some of the intro code.Would this allow for skipping parameters, or would it create the 2^N scenario? Or would that not matter?One possibility would be allowing explicit creation of stubs e.g. Foo(int a, int b) { return a+b; } stub(int a) { b=0; } stub(real r) { a=cast(int)r; b=cast(int)r*2; } or something like that
Aug 04 2005
Don Clugston <Don_member pathlink.com> wrote: [...]but there's no way to change line (C) to say 'use the default argument'.[...] This is the crucial point. My first thought was, to declare the function variable as int function( int, ...) f; But this does not work, because D seems to treat the ellipsis as a type of its own, which is not documented anywhere. If this is not a bug, the typesystem of D seems to be broken at at least two points: variadic and default parameters. -manfred
Aug 03 2005