August 27, 2012
On Monday, 27 August 2012 at 13:44:43 UTC, Manu wrote:
> On 27 August 2012 15:48, Robert Clipsham <robert@octarineparrot.com> wrote:
>
>> I seem to recall I looked at this issue myself at one point. It goes
>> something like:
>> ----
>>
>> auto foo = (int a = 1) { return a; };
>> auto bar = (int a) { return a; };
>> ----
>> int function(int) is mangled exactly the same as int function(int = 1) as
>> default args aren't used for mangling. dmd does semantic analysis on the
>> type of foo, which returns int function(int = 1), which is mangled as int
>> function(int) and stored in dmd's hashmap of types (default args aren't
>> mangled). When the semantic analysis of bar is done it checks the hashmap,
>> sees that the type is already there (has the same name mangling) and does
>> not repeat semantic analysis. If you switch the order of declarations then
>> the opposite happens - the default arg is ignored.
>>
>
> Cached in a hashmap! precisely what I suspected (without knowing anything
> about it) ;)
> That explains why Walter keeps going on about the name mangling. It's all
> clear.

This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.
August 27, 2012
On 27 August 2012 16:23, kenji hara <k.hara.pg@gmail.com> wrote:

> I think that the function type and its mangled name must not contain the default args, then I can agree with Walter at the point.
>
> But, I think the variables of function pointers and delegates still
> can have default args as a part of their declarations.
> (It will be a part of VarDeclaration, not a part of TypeFunction)
> In following case, the default arg looks like a part of the type, but
> actually is a part of the declaration of fp.
>
> void function(int n = 10) fp;
> pragma(msg, typeof(fp));  // should print void function(int), because
> default args are not a part of type.
> fp();  // I think this can be allowed, because fp can remember that
> the first parameter has the default argument.
>
> But, it seems to me there are some corner cases.
>
>   // fp will *inherit* default args from its initializer, or not?
>   auto fp = (int n = 10){}
>

I'd say, will.

  // what is the actual default arg of fp?
>   void function(int n = 10) fp1 = (int n = 20){}
>
  void function(int n) fp2 = (int n = 30){}
>   void function(int n = 40) fp3 = (int n){}
>

The declaration (on the left) is correct in all cases. It was explicitly stated when defining the variable.

  // fp has ambiguous default arg, or has no default arg?
>   auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ;
>

Finally, a REAL conundrum! :) .. Not sure what to tell you... Error? Warning, and remove the default arg since there's a disagreement?


>   // more complicated case, first defarg is same, then it will be
> *inherited*?
>   auto fp2 = some_runtime_condition ? (int n = 10, string s =
> "hello"){} : (int n = 10, string s = "world"){} ;
>

Same problem as above. I'd say hard error, since this is a) extremely unlikely! And b) it's obvious there's no 'correct' conclusion anyway. The user writing that code clearly imagines that D is a dynamic language, and doesn't properly understand the nature of a compiled language.

  int function(int n = 10) fp;   // default arg of the first parameter is 10
>   fp = (int n = 20){ return n; }  // function literal's default arg
> will be ignored (in my opinion), is this expected?
>

Agree, the variable was already defined by the users explicit request.

  // returning function pointer/delegate type can have default args?
>   int delegate(int n = 10) foo(int x) { ... }
>

Sure, why not? If the recipient is auto, then it should inherit those properties.

If we can take agreements each other about them, it may be supported.
>

+1 for Kenji :)


August 27, 2012
On Sun, 26 Aug 2012 18:26:40 -0400, Manu <turkeyman@gmail.com> wrote:

> I just updated to 2.60 and found errors throughout my code where function
> pointers default args no longer work.
> *Every single project* I've written in D, 5 projects, don't work anymore,
> including my projects at work.

OK, I've read all the posts on this, and this is what I think would solve the problem:

1. default parameters are part of the function pointer type, because a function pointer has a type.
2. The mangled name of the function that is assigned to that function pointer does *not* have default parameters mangled in.  Only the function pointer type has them.
3. Since the default parameters are part of the type, but not defined by the function it points to, you can use interchangeably functions of the same type which define default parameters or not (or define different ones).  The default parameters follow the function pointer variable.
4. If a template instantiates with a function type that takes a default parameter, that default parameter is mangled as part of the template type.  There is no way around this.  But the mangling of the default parameter is *separate* from the mangling of the function type.

We can probably implement this as a library feature, but I lack the full template-fu skills to do it.  It would be nice to have the compiler do this for us, but I don't know the work involved.  Here is a pseudocode type we may be able to use:

struct DefaultParamFuncPtr(F, alias T...) if(is(F == function) && isValues!T) // no idea how to define isValues
{
   F funcptr;
   static T defaultparams;
   alias funcptr this; // allow this type to act as a straight F, or accept an F to rebind to a different function
   typeof(funcptr()) opCall(X...)(X params) if(X is a prefix of F's args) // no idea how to write this constraint :)
   {
      // construct the rest of the tuple for params given defaultparams
      typeof(F's args) realtuple = params ~ defaultparams[params.length..$]; // construct the full call parameters
      return funcptr(realtuple);
   }
}

Yeah, it would be really nice to do this in the compiler, but I think the above describes what I would consider to be an equivalent type.  The default parameters live as metadata in the type of the function pointer, but are separate from the function type that it points to.

-Steve
August 27, 2012
On 27 August 2012 17:03, foobar <foo@bar.com> wrote:

> This discussion is all sorts of wrong. Whoever said that defargs are metadata (Manu?) was right. Therefore it would make sense to implement a general metadata facility for D and use *that* for defargs. C++11 has annotations, so that's the place to start looking at. D has already enough of those pesky special case features, let's not add yet another one at the expense of a more general metadata/annotation mechanism which eventually would be added anyway due to popular demand.
>

Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)


August 27, 2012
On 8/27/12 7:03 AM, Manu wrote:
>     If we can take agreements each other about them, it may be supported.
>
> +1 for Kenji :)

I think the matter here is that we're looking at adding very significant complexity to the language for a feature with at best minor, marginal benefits.

Default arguments add no power to the language, you can quote me on that.

A wrong was fixed with a simplifying solution. That broke some code. Granted, the code could be made to work with a much more complicated (and complicating) solution. Is it worth it? At this point in D's development, do we want to go full-bore about something that complicates the language to very little benefit, particularly considering we already have so many balls in the air? I think it's reasonable to look into fixing the code and calling it a day.


Thanks,

Andrei
August 27, 2012
On 8/27/12 7:34 AM, Manu wrote:
> On 27 August 2012 17:03, foobar <foo@bar.com <mailto:foo@bar.com>> wrote:
>
>     This discussion is all sorts of wrong. Whoever said that defargs are
>     metadata (Manu?) was right. Therefore it would make sense to
>     implement a general metadata facility for D and use *that* for
>     defargs. C++11 has annotations, so that's the place to start looking
>     at. D has already enough of those pesky special case features, let's
>     not add yet another one at the expense of a more general
>     metadata/annotation mechanism which eventually would be added anyway
>     due to popular demand.
>
>
> Yes, I suggested this as a possibility above, but nobody commented. It
> seems like it might solve 2 problems with one stone ;)

Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example.

Andrei
August 27, 2012
On 08/27/2012 03:23 PM, kenji hara wrote:
> I think that the function type and its mangled name must not contain
> the default args, then I can agree with Walter at the point.
>
> But, I think the variables of function pointers and delegates still
> can have default args as a part of their declarations.
> (It will be a part of VarDeclaration, not a part of TypeFunction)
> In following case, the default arg looks like a part of the type, but
> actually is a part of the declaration of fp.
>
> void function(int n = 10) fp;
> pragma(msg, typeof(fp));  // should print void function(int), because
> default args are not a part of type.
> fp();  // I think this can be allowed, because fp can remember that
> the first parameter has the default argument.
>
> But, it seems to me there are some corner cases.
>
>    // fp will *inherit* default args from its initializer, or not?
>    auto fp = (int n = 10){}
>

I assume we'd like it to do that lest the user has to supply the type
explicitly. It might lead to funny interactions though.

>    // what is the actual default arg of fp?
>    void function(int n = 10) fp1 = (int n = 20){}
>    void function(int n) fp2 = (int n = 30){}
>    void function(int n = 40) fp3 = (int n){}
>

The declared default arg overrides.

I'd suggest to make it an error when it can be fixed by leaving out the
default argument from a function literal occurring within the
expression.

>    // fp has ambiguous default arg, or has no default arg?
>    auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ;
>    // more complicated case, first defarg is same, then it will be *inherited*?
>    auto fp2 = some_runtime_condition ? (int n = 10, string s =
> "hello"){} : (int n = 10, string s = "world"){} ;
>

I'd say, never attempt to combine default arguments in any way.

It is not required to make it a hard error in all cases, because the
error will occur when the user attempts to rely upon the non-existent
default argument. By the argument from above, both of those should
be in error.


>    int function(int n = 10) fp;   // default arg of the first parameter is 10
>    fp = (int n = 20){ return n; }  // function literal's default arg
> will be ignored (in my opinion), is this expected?
>

This specific expression should be in error, but it should probably be
possible to do

static int foo(int n=10){ return n; }
int function(int = 20) fp = &foo;

The reason why this should work is because adding a default argument
should be as unlikely as possible to break existing code.


>    // returning function pointer/delegate type can have default args?
>    int delegate(int n = 10) foo(int x) { ... }
>

Seems easy enough to support for consistency.

> If we can take agreements each other about them, it may be supported.
>

I personally do not rely on them.

August 27, 2012
On 08/27/2012 04:53 PM, Andrei Alexandrescu wrote:
> On 8/27/12 7:34 AM, Manu wrote:
>> On 27 August 2012 17:03, foobar <foo@bar.com <mailto:foo@bar.com>> wrote:
>>
>>     This discussion is all sorts of wrong. Whoever said that defargs are
>>     metadata (Manu?) was right. Therefore it would make sense to
>>     implement a general metadata facility for D and use *that* for
>>     defargs. C++11 has annotations, so that's the place to start looking
>>     at. D has already enough of those pesky special case features, let's
>>     not add yet another one at the expense of a more general
>>     metadata/annotation mechanism which eventually would be added anyway
>>     due to popular demand.
>>
>>
>> Yes, I suggested this as a possibility above, but nobody commented. It
>> seems like it might solve 2 problems with one stone ;)
>
> Whilst I agree a metadata facility is an interesting topic to look into,
> I think default arguments for functions is a poor motivating example.
>
> Andrei

+1.
August 27, 2012
On Mon, 27 Aug 2012 09:23:54 -0400, kenji hara <k.hara.pg@gmail.com> wrote:

> I think that the function type and its mangled name must not contain
> the default args, then I can agree with Walter at the point.
>
> But, I think the variables of function pointers and delegates still
> can have default args as a part of their declarations.
> (It will be a part of VarDeclaration, not a part of TypeFunction)
> In following case, the default arg looks like a part of the type, but
> actually is a part of the declaration of fp.

I think the type of the function pointer and the type (and mangling) of the function are two separate types.  One is a function pointer, one is a function.

You only need to mangle the function, not the pointer type.

Consider this rough equivalent (not sure how to write the constraints):

struct callWithN(F, int n) if(F is a function which takes an int as its last arg)
{
   F funcptr;
   auto opCall(T...)(T t) if(T is equivalent to F's args)
   {
      return funcptr(t);
   }
   auto opCall(T...)(T t) if(T is equivalent to F's args, except for last int arg)
   {
      return funcptr(t, n);
   }
}

I think the above (with correct template constraints) is equivalent to defining a function pointer with a default integer argument.  Note that the function it points to has *nothing* to do with default args, and so should be mangled like any other function.

So I think the default args have to be part of the pointer type, just not the function type.

> void function(int n = 10) fp;
> pragma(msg, typeof(fp));  // should print void function(int), because
> default args are not a part of type.

No.  They are part of the function pointer type.

>   // fp will *inherit* default args from its initializer, or not?
>   auto fp = (int n = 10){}

Yes, this should be equivalent to:

void function(int = 10) fp = (int n){};

>   // what is the actual default arg of fp?
>   void function(int n = 10) fp1 = (int n = 20){}

compiler error. Even if it should be able to reassign a default arg 10 to a default arg 20 function pointer, allowing the above to compile would be extremely confusing, and there is no real reason to allow it.  Use the property of the function pointer that gets the function pointer type without the default params (see below).

>   void function(int n) fp2 = (int n = 30){}

30

>   void function(int n = 40) fp3 = (int n){}

40

>
>   // fp has ambiguous default arg, or has no default arg?
>   auto fp1 = some_runtime_condition ? (int n = 10){} : (int n = 20){} ;

Compiler error.  You can't define the type differently based on a runtime condition.

>   // more complicated case, first defarg is same, then it will be *inherited*?
>   auto fp2 = some_runtime_condition ? (int n = 10, string s =
> "hello"){} : (int n = 10, string s = "world"){} ;

Again, two different types.  Error.

>
>   int function(int n = 10) fp;   // default arg of the first parameter is 10
>   fp = (int n = 20){ return n; }  // function literal's default arg
> will be ignored (in my opinion), is this expected?

I think actually, this should be a compiler error, but it would be a valid choice to make this simply ignore the secondary default arg.

Here is what I would like to happen:

Given two sets of default parameter tuples X and Y, where X is not a prefix of Y, and Y is not a prefix of X, a function pointer with default args X cannot be assigned from a function pointer with default args Y.
Given X and Y where X is a prefix of Y, a function pointer with default args Y can be assigned from a function pointer with default args X.  However, the opposite will not be true.
However, we should define a function pointer property (e.g. funcptr) which gives naked access to the underlying function pointer.

So if you had fx with default args X, and fy with default args Y, and both pointed at functions with the same parameter types, then you could do:

fx.funcptr = fy.funcptr;
fy.funcptr = fx.funcptr;

If this is too complicated, then just allowing all functions with the same underlying function parameter types to be freely assignable also is a valid (but IMO more confusing) choice.

Given the above, I think it would be prudent to have the default args returnable via a property or a __traits command.  e.g.:

auto tupleOfDefaultArgs = fx.defargs;

>
>   // returning function pointer/delegate type can have default args?
>   int delegate(int n = 10) foo(int x) { ... }

Of course.  Default args are part of the pointer type.

-Steve
August 27, 2012
On 27 August 2012 17:51, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote:

> On 8/27/12 7:03 AM, Manu wrote:
>
>>     If we can take agreements each other about them, it may be supported.
>>
>> +1 for Kenji :)
>>
>
> I think the matter here is that we're looking at adding very significant complexity to the language for a feature with at best minor, marginal benefits.
>

... what?
We're not talking about *adding* anything, we're talking about a bug.
The scenario given in the bug report isn't even theoretically flawed, it
should work fine under the existing design (hence the bug report), but it
seems some design changes may a) make more sense, and also b) simplify
implementation/fixing the bug.
That seems fine, but rather than fixing said implementation bug, the
feature was just removed from the language, which was coincidentally a
major breaking change.

Does the dmd implementation define the D language?

I'm sure there's lots of bugs in various features that could be solved by
removing the feature completely.
I encounter problems using 'ref' and 'in' all the time...

The feature was obviously intended, someone took the time to put it there
in the first place, and people use it.
It's implementation is apparently not perfect, and needs to be clarified,
maybe that requires a design tweak. That seems more what Kenji is talking
about to me.


Default arguments add no power to the language, you can quote me on that.
>

Did I mention that *every project* I've written in D is broken? Why have I
used a feature so much if it offers nothing?
The fact the bug even exists implies that someone saw value in the feature
in the first place...

I'm not sure how you define 'power', but default args certainly offer
convenience. It also allows the default to be changed universally in one
place.
I wouldn't want to find the location of all calls to the function, and then
reason whether their manual placement of the default arg at each instance
is as a conventional default (such that I should update it to the new
default), or whether they explicitly intend that value in this particular
case.


A wrong was fixed with a simplifying solution. That broke some code.
> Granted, the code could be made to work with a much more complicated (and complicating) solution. Is it worth it? At this point in D's development, do we want to go full-bore about something that complicates the language to very little benefit, particularly considering we already have so many balls in the air? I think it's reasonable to look into fixing the code and calling it a day.
>

A 'wrong'? What a subjective opinion. Default args are useful, and I
absolutely appreciated the uniformity D seemed to offer in this respect
(ie, all kinds of function definition supported them).
How do you define 'fixing the code'? The 'fix' is a major breaking
change. Usually people are extremely phobic of the possibility of breaking
changes in even minor cases, yet this is relatively severe.

Far more important changes have been rejected on the grounds that they're breaking changes alone...

If a solid solution is too difficult to implement, or isn't worth the time right now, then just put the bug back. It was such a minor bug in a very contrived case, and better than the fix.


On 27 August 2012 17:53, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>
 wrote:

> On 8/27/12 7:34 AM, Manu wrote:
>
>> Yes, I suggested this as a possibility above, but nobody commented. It seems like it might solve 2 problems with one stone ;)
>>
>
> Whilst I agree a metadata facility is an interesting topic to look into, I think default arguments for functions is a poor motivating example.


I agree. It was just an interesting parallel that I noticed.