www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Request: Implement default arguments properly (unlike C).

reply Don Clugston <Don_member pathlink.com> writes:
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
next sibling parent reply AJG <AJG_member pathlink.com> writes:
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
next sibling parent reply Don Clugston <Don_member pathlink.com> writes:
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).

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
next sibling parent reply AJG <AJG_member pathlink.com> writes:
Hi,

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.

I suppose this is a valid assumption re: optimizer.
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.

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 I thought you wanted to move beyond "C++ semantics?" If we're to do this, I think we should get rid of skipping restrictions.
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).

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.

But skipping parameters would be soooooooo nice... ;)
B) Within a class, you can't use member vars as defaults (Ditto).

that, because when the function is created, you have full access to the class which created it.

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.
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.
PS: AFAIK, unlike C++, C doesn't have default parameters.


<jealous> You haven't missed much. We have booleans now. </jealous> Cheers, --AJG.
Aug 03 2005
next sibling parent Don Clugston <Don_member pathlink.com> writes:
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 ;).

Me too. But I'm less confident that I'll get it. Until someone manages to clone Walter.
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++,

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.

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.
A) When calling, you can't skip any defaults (Very annoying).

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.

But skipping parameters would be soooooooo nice... ;)

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.
B) Within a class, you can't use member vars as defaults (Ditto).

that, because when the function is created, you have full access to the class which created it.

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.
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.

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.
PS: AFAIK, unlike C++, C doesn't have default parameters.


<jealous> You haven't missed much. We have booleans now. </jealous>

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
Aug 04 2005
prev sibling next sibling parent Don Clugston <Don_member pathlink.com> writes:
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 ;).

Me too. But I'm less confident that I'll get it. Until someone manages to clone Walter.
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++,

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.

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.
A) When calling, you can't skip any defaults (Very annoying).

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.

But skipping parameters would be soooooooo nice... ;)

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.
B) Within a class, you can't use member vars as defaults (Ditto).

that, because when the function is created, you have full access to the class which created it.

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.
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.

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.
PS: AFAIK, unlike C++, C doesn't have default parameters.


<jealous> You haven't missed much. We have booleans now. </jealous>

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
Aug 04 2005
prev sibling parent reply Deewiant <deewiant.doesnotlike.spam gmail.com> writes:
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. So this is my #1 gripe related to default arguments. Inability to use members as defaults in a class, which you already brought earlier, would be #2. #3 is the problem with function pointers in the original post, but 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
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

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)

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.
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.
So this is my #1 gripe related to default arguments.
Inability to use members as defaults in a class, which you already
brought earlier, would be #2.
#3 is the problem with function pointers in the original post, but
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: # Foo(arg1, , arg3, , arg5); Or with a "default" or "skip" keyword: # Foo(arg1, default, arg3, default, arg5); Or perhaps with a symbol: # Foo(arg1, ?, arg3, ?, arg5); 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
parent reply Georg Bauhaus <bauhaus futureapps.de> writes:
AJG wrote:

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.

int foo(int x, int y = foo(x)) what now? -- Georg
Aug 04 2005
parent reply AJG <AJG_member pathlink.com> writes:
Hi,

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.

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. So the point is moot. E.g.: # int foo(int x = foo()) {} Cheers, --AJG.
Aug 04 2005
parent Georg Bauhaus <bauhaus futureapps.de> writes:
AJG wrote:

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.

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); }
Aug 04 2005
prev sibling parent "Ben Hinkle" <ben.hinkle gmail.com> writes:
"Don Clugston" <Don_member pathlink.com> wrote in message 
news:dcs261$2ad4$1 digitaldaemon.com...
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.

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).
Aug 04 2005
prev sibling parent reply BCS <BCS_member pathlink.com> writes:
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
parent reply AJG <AJG_member pathlink.com> writes:
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
parent reply BCS <BCS_member pathlink.com> writes:
In article <dcs6d5$2ef3$1 digitaldaemon.com>, AJG says...

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?

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 that
Aug 04 2005
parent AJG <AJG_member pathlink.com> writes:
Hi,

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?

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)

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.
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
prev sibling parent Manfred Nowak <svv1999 hotmail.com> writes:
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