April 26, 2012
On 04/26/2012 11:54 AM, Don Clugston wrote:
> On 26/04/12 11:21, Timon Gehr wrote:
>> On 04/26/2012 09:54 AM, Walter Bright wrote:
>>> On 4/26/2012 12:47 AM, Timon Gehr wrote:
>>>> On 04/26/2012 05:44 AM, Walter Bright wrote:
>>>>> A subtle but nasty problem - are default arguments part of the
>>>>> type, or
>>>>> part of the declaration?
>>>>>
>>>>> See http://d.puremagic.com/issues/show_bug.cgi?id=3866
>>>>>
>>>>> Currently, they are both,
>>>>
>>>> That is how it should be.
>>>>
>>>>> which leads to the nasty behavior in the bug report.
>>>>>
>>>>
>>>> It contributes, but it is not the main cause.
>>>>
>>>>> The problem centers around name mangling. If two types mangle the
>>>>> same,
>>>>> then they are the same type.
>>>>
>>>> Then they are equal types.
>>>
>>> This is simply not tenable. What defines when they are "equal" types and
>>> when they are "not equal"?
>>
>> This is a matter of terminology. For example, for 'equal' just exclude
>> the default parameters from the comparison. For 'the same' include
>> default parameters in the comparison. (therefore, 'the same' implies
>> 'equal')
>
> The language doesn't have the concepts of "same" and "equal" with
> respect to types. There is "equal" and "implicitly converts to", but
> that's not quite the same.
>
>>>> The schizophrenic behavior occurs because the types cross-talk. Are
>>>> mangled
>>>> names kept unique in the compiler or what is the implementation issue
>>>> exactly?
>>>
>>> It's a conceptual issue. When is one type the same as another, and when
>>> is it not?
>>>
>>
>> void function(int) is the same as void function(int) and both are equal
>> void function(int=2) is not the same as void function(int=3), but both
>> are equal.
>
> The question was *when* are they same, not how you name them.

I think I have answered that. Anyway, probably it is indeed a good idea to get rid of default parameters for delegates and function pointers.
The issues are certainly resolvable but the complexity of the resulting feature could not be justified.
April 26, 2012
On 26/04/12 12:11, Timon Gehr wrote:
> On 04/26/2012 11:46 AM, Don Clugston wrote:
>> On 26/04/12 11:28, Timon Gehr wrote:
>>> On 04/26/2012 10:51 AM, Don Clugston wrote:
>>>> On 26/04/12 05:44, Walter Bright wrote:
>>>>> A subtle but nasty problem - are default arguments part of the
>>>>> type, or
>>>>> part of the declaration?
>>>>>
>>>>> See http://d.puremagic.com/issues/show_bug.cgi?id=3866
>>>>>
>>>>> Currently, they are both, which leads to the nasty behavior in the bug
>>>>> report.
>>>>>
>>>>> The problem centers around name mangling. If two types mangle the
>>>>> same,
>>>>> then they are the same type. But default arguments are not part of the
>>>>> mangled string. Hence the schizophrenic behavior.
>>>>>
>>>>> But if we make default arguments solely a part of the function
>>>>> declaration, then function pointers (and delegates) cannot have
>>>>> default
>>>>> arguments. (And maybe this isn't a bad thing?)
>>>>
>>>> I think it is a mistake to allow default arguments in function pointers
>>>> and delegates (it's OK for delegate literals, there you have the
>>>> declaration).
>>>
>>> The parenthesised part is in conflict with your other statement.
>>
>> No it doesn't. A default argument is a delegate literal is part of the
>> declaration, not part of the type.
>
> If types cannot specify default arguments, then those will be thrown
> away right away, because what is later called is based on the type of
> the delegate and not on the implicit function declaration that has its
> address taken. What is the point of allowing it if it cannot be used?

Fair point. It could be used in the case where it is called at the point of declaration (I do that a fair bit), but it's pretty much useless because it is clearer code to put the default parameter in the call.

int m = (int a, int b = 3){ return a+b;}(7);

Point conceded. So default arguments should be disallowed in delegate literals as well.
April 26, 2012
I've always thought of default arguments to be plain syntactic sugar, so for void f(int i, int j=5)    f(1) is simply transformed to f(1,5) and the rest is the same.
April 26, 2012
On 2012-04-26 03:44:07 +0000, Walter Bright <newshound2@digitalmars.com> said:

> A subtle but nasty problem - are default arguments part of the type, or part of the declaration?
> 
>     See http://d.puremagic.com/issues/show_bug.cgi?id=3866
> 
> Currently, they are both, which leads to the nasty behavior in the bug report.
> 
> The problem centers around name mangling. If two types mangle the same, then they are the same type. But default arguments are not part of the mangled string. Hence the schizophrenic behavior.
> 
> But if we make default arguments solely a part of the function declaration, then function pointers (and delegates) cannot have default arguments. (And maybe this isn't a bad thing?)

Assuming we want to allow it, all you need is to treat the type having the default parameters as a specialization of the type that has none. In other words, the type with the default arugments is implicitly castable to the type without.

Should it affect name mangling? Yes and not. It should affect name mangling in the compiler since you're using the mangled name to check for equality between types. But I think it should not affect name mangling for the generated code: the base type without the default arguments should give its name to the emitted symbols so that changing the default argument does not change the ABI.

But is it desirable? I'm not convinced. I don't really see the point. It looks like a poor substitute for overloading to me.

That said, there was some talk about adding support for named parameters a year ago. For named parameters, I think it'd make sense to have parameter names be part of the type, and little harm would result in adding default parameters too into the mix. As suggested above, it should be implicitly castable to a base type without parameter names or default values. But for default parameters alone, I don't feel the complication is justified.


-- 
Michel Fortin
michel.fortin@michelf.com
http://michelf.com/

April 26, 2012
Le 26/04/2012 05:44, Walter Bright a écrit :
> A subtle but nasty problem - are default arguments part of the type, or
> part of the declaration?
>
> See http://d.puremagic.com/issues/show_bug.cgi?id=3866
>
> Currently, they are both, which leads to the nasty behavior in the bug
> report.
>
> The problem centers around name mangling. If two types mangle the same,
> then they are the same type. But default arguments are not part of the
> mangled string. Hence the schizophrenic behavior.
>
> But if we make default arguments solely a part of the function
> declaration, then function pointers (and delegates) cannot have default
> arguments. (And maybe this isn't a bad thing?)

Maybe the problem is that type are considered to be the same when the mangling is the same. The default parameter is part of the type, isn't mangled (or hell will come on earth) and is covariant with the type with no default.
April 26, 2012
Walter:
> A subtle but nasty problem - are default arguments part of the type, or part of the declaration?

I'm waiting for years to see you finally facing this problem too :-) The current situation is not acceptable, so some change of the current situation is required, probably a small breaking change.

The simplest solution is to the breaking change of disallowing default arguments for function pointers and delegates. This also means disallowing talking the pointer/delegate of function with default arguments. The advantage of this solution is its simplicity, for both the compiler the D programmer and the D newbie that has to learn D.

A second alternative solution is to put the default arguments inside the function mangled and, and to avoid troubles, allow this only for POD data known at compile-time (and throw a compile-time error in all other cases). This has the advantage that common default arguments like "true", 15, 2.5, are accepted, so this gives some flexibility. One disadvantage is that you need to add a special case to D, but it's not a nasty special case, it's a clean thing.

A third possible way is to put the default arguments inside the delegate/function itself, as in Python. So default arguments for delegates/function have a different semantics. Then add a hidden extra field to such delegates/functions, a ubyte that tells the function how many arguments are actually given to the function (so the function is later able to use this argument to know how many arguments replace with the default ones). This has some disadvantages, because you can retrofit already compiled modules, so you can't get the function pointer of a function that doesn't have this hidden argument. The advantage is that the semantics is clean, and it's quite flexible.

Bye,
bearophile
April 26, 2012
Michel Fortin:

> That said, there was some talk about adding support for named parameters a year ago.

Good reminder. I think such parts of D shouldn't be designed one
of a time. If you want to face the problem of default arguments,
it's better to think about named arguments too (even if you don't
want to implement them now, to not preclude their good future
implementation).

Bye,
bearophile
April 26, 2012
On Wed, 25 Apr 2012 23:44:07 -0400, Walter Bright <newshound2@digitalmars.com> wrote:

> A subtle but nasty problem - are default arguments part of the type, or part of the declaration?
>
>     See http://d.puremagic.com/issues/show_bug.cgi?id=3866
>
> Currently, they are both, which leads to the nasty behavior in the bug report.
>
> The problem centers around name mangling. If two types mangle the same, then they are the same type. But default arguments are not part of the mangled string. Hence the schizophrenic behavior.
>
> But if we make default arguments solely a part of the function declaration, then function pointers (and delegates) cannot have default arguments. (And maybe this isn't a bad thing?)

Some testing (2.059):

void main()
{
    auto a = (int x = 1) { return x;};
    auto b = (int x) { return x;};
    pragma(msg, typeof(a).stringof);
    pragma(msg, typeof(b).stringof);
}

output:

int function(int x = 1) pure nothrow @safe
int function(int x = 1) pure nothrow @safe

second pass:

void main()
{
    auto a = (int x = 1) { return x;};
    pure nothrow @safe int function(int) b = (int x) { return x;};
    pragma(msg, typeof(a).stringof);
    pragma(msg, typeof(b).stringof);
    b = a; // ok
    //a = b; // error

    //b(); // error
}

output:

int function(int x = 1) pure nothrow @safe
int function(int)


if you ask me, everything looks exactly as I'd expect, except the auto type inference of b.  Can this not be fixed?  I don't understand the difficulty.

BTW, I didn't know you could have default arguments for functions/delegates, it's pretty neat :)

-Steve
April 26, 2012
On 26/04/12 05:44, Walter Bright wrote:
> But if we make default arguments solely a part of the function declaration, then
> function pointers (and delegates) cannot have default arguments. (And maybe this
> isn't a bad thing?)

I can't see disallowing default arguments as being a good thing.  For example, instead of,

    void foo(int a, int b = 2)
    {
        ...
    }

surely I can just put instead

    void foo(int a, int b)
    {
        ...
    }

    void foo(int a)
    {
        foo(a, 2);
    }

... and surely I can do something similar for function pointers and delegates. So, I can still have default arguments in effect, I just have to work more as a programmer, using a less friendly and easy-to-understand syntax.  That doesn't really seem like a good way to operate unless there's an extreme level of complication in getting the compiler to handle the situation.
April 26, 2012
On Thu, 26 Apr 2012 09:08:07 -0400, Steven Schveighoffer <schveiguy@yahoo.com> wrote:

> void main()
> {
>      auto a = (int x = 1) { return x;};
>      pure nothrow @safe int function(int) b = (int x) { return x;};
>      pragma(msg, typeof(a).stringof);
>      pragma(msg, typeof(b).stringof);
>      b = a; // ok
>      //a = b; // error
>
>      //b(); // error
> }
>
> output:
>
> int function(int x = 1) pure nothrow @safe
> int function(int)
>

Nevermind, I just realized it was ignoring my pure nothrow @safe for the declaration.  Moving it after the declaration results in:

void main()
{
    auto a = (int x = 1) { return x;};
    int function(int) pure nothrow @safe b = (int x) { return x;};
    pragma(msg, typeof(a).stringof);
    pragma(msg, typeof(b).stringof);
}

output:

int function(int x = 1) pure nothrow @safe
int function(int x = 1) pure nothrow @safe

which clearly mimics the auto behavior.  This is *really* no good, since it seems to be ignoring the explicit type that I specified.

IMO, the correct solution is to make the default argument part of the type (and don't let it affect things globally!), and make it derived from the version without a default arg.  I think Michel Fortin said the same thing.

-Steve