www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Dream Feature Regarding Default Arguments

reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
I realize this isn't the time for such a thing to be added to D, but I 
thought I'd put the idea out there, FWIW:

I find myself frequently needing to design APIs that work like this:

     func( [optionalFoo], [optionalBar] )

Typically, that's not permitted in C-style languages, including D. They 
only allow this:

     func( [optionalFoo, [optionalBar]] )

This restriction is necessary because, if the params take compatible 
types (ex: func(int,int)) and the caller only provides one, then there's 
no other way to tell which parameter the caller was trying to provide.

But when the params are different incompatible types, there's no 
ambiguity. This leads to the following awkward, noisy, hard-to-read 
idioms which I find myself using very frequently (I'm pretty sure I've 
seen it in Phobos, too):

     void func(Foo foo=defaultFoo, Bar bar=defaultBar) {...}
     void func(Bar bar) {
         func(defaultFoo, bar);
     }

or:

     void func(Foo foo, Bar bar=defaultBar) {...}
     void func(Bar bar=defaultBar) {
         func(defaultFoo, bar);
     }

Worse still, if there are other overloads, params, templates, etc, then 
this gets REALLY hairy, REALLY fast. It becomes very difficult to read, 
difficult to test, difficult to maintain, and difficult for users to 
grok the function's generated documentation.

Such a big mess for such a trivially simple API:

     func( [optionalFoo], [optionalBar] )

It would be *fantastic* if D recognized the disambiguation of using 
incompatible types and permitted this:

     interface Foo {}
     interface Bar {}
     class FooBar : Foo, Bar {}

     void func(Foo foo=someFoo, Bar bar=someBar) {...}

     func(myFoo); // Unambiguous, OK
     func(myBar); // Unambiguous, OK
     func(myFooBar); // Ambiguous, ERROR
Apr 05 2014
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 4/5/2014 9:26 PM, Nick Sabalausky wrote:
 It would be *fantastic* if D recognized the disambiguation of using
 incompatible types and permitted this:

      interface Foo {}
      interface Bar {}
      class FooBar : Foo, Bar {}

      void func(Foo foo=someFoo, Bar bar=someBar) {...}

      func(myFoo); // Unambiguous, OK
      func(myBar); // Unambiguous, OK
      func(myFooBar); // Ambiguous, ERROR
Actually, that last line should be: func(myFooBar); // Unambiguous, this is still interpreted as the first parameter (with the second parameter left as default) just as it is right now.
Apr 05 2014
parent "Mason McGill" <mmcgill caltech.edu> writes:
On Sunday, 6 April 2014 at 01:33:36 UTC, Nick Sabalausky wrote:
 On 4/5/2014 9:26 PM, Nick Sabalausky wrote:
 It would be *fantastic* if D recognized the disambiguation of 
 using
 incompatible types and permitted this:

     interface Foo {}
     interface Bar {}
     class FooBar : Foo, Bar {}

     void func(Foo foo=someFoo, Bar bar=someBar) {...}

     func(myFoo); // Unambiguous, OK
     func(myBar); // Unambiguous, OK
     func(myFooBar); // Ambiguous, ERROR
Actually, that last line should be: func(myFooBar); // Unambiguous, this is still interpreted as the first parameter (with the second parameter left as default) just as it is right now.
I think D is actually one of the better languages around in terms of factoring this sort of thing out. Here's an example argument parser that lets you define pairs of optional arguments: http://pastebin.com/RaNfwH6X. It can be extended to allow n-tuples of optional arguments. Argument parsing is encapsulated so writers only have to declare one function template, with a constraint that clearly expresses intent: void func(Args...)(Args args) if (Pattern!(bool, AnyOf!(int, string)).matches!Args) { alias Parser = Pattern!(bool, AnyOf!(int, string)); auto parsedArgs = Parser.parse(tuple(args), tuple(false, 0, "0")); writeln(parsedArgs); } void main() { func(true); // true, 0, "0" func(true, 1); // true, 1, "0" func(true, "1"); // true, 0, "1" func(true, 1, "1"); // true, 1, "1" }
Apr 06 2014
prev sibling next sibling parent reply "JN" <666total wp.pl> writes:
On Sunday, 6 April 2014 at 01:26:21 UTC, Nick Sabalausky wrote:
 Such a big mess for such a trivially simple API:

     func( [optionalFoo], [optionalBar] )

 It would be *fantastic* if D recognized the disambiguation of 
 using incompatible types and permitted this:

     interface Foo {}
     interface Bar {}
     class FooBar : Foo, Bar {}

     void func(Foo foo=someFoo, Bar bar=someBar) {...}

     func(myFoo); // Unambiguous, OK
     func(myBar); // Unambiguous, OK
     func(myFooBar); // Ambiguous, ERROR
Wouldn't it be better to have named parameters like in Python or func(foo=myFoo) or func(bar=myBar) or func(foo=myFoo, bar=myBar) and there would be no ambiguity.
Apr 06 2014
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
On 4/6/2014 10:47 AM, JN wrote:
 Wouldn't it be better to have named parameters like in Python or


 func(foo=myFoo) or func(bar=myBar) or func(foo=myFoo, bar=myBar)
 and there would be no ambiguity.
Named parameters would be a good compliment to this (and I have always wanted named parameters in D), but even with them it would still be nice to be able have neat-n-tidy implementations of func([foo],[bar]) APIs that don't require the use of named params.
Apr 06 2014
parent reply "Frustrated" <Who where.com> writes:
On Sunday, 6 April 2014 at 21:06:44 UTC, Nick Sabalausky wrote:
 On 4/6/2014 10:47 AM, JN wrote:
 Wouldn't it be better to have named parameters like in Python 
 or


 func(foo=myFoo) or func(bar=myBar) or func(foo=myFoo, 
 bar=myBar)
 and there would be no ambiguity.
Named parameters would be a good compliment to this (and I have always wanted named parameters in D), but even with them it would still be nice to be able have neat-n-tidy implementations of func([foo],[bar]) APIs that don't require the use of named params.
How bout func(,,x,,y) or func(default, default, x, default, default, y)? where default is a keyword that substitutes the default value.
Apr 06 2014
next sibling parent "Nick Sabalausky" <a a.a> writes:
On Sunday, 6 April 2014 at 23:08:10 UTC, Frustrated wrote:
 How bout

 func(,,x,,y)
That isn't too bad (VB6 had it IIRC), but the caller still has to care about the order of the params they're ignoring. Ie if you have func([foo],[bar]) then you can't just pass in a one-param foo-only or bar-only without worrying about the unimportant detail of the arbitrary ordering (unlike with the overload approach).
Apr 06 2014
prev sibling parent =?UTF-8?B?Ik5vcmRsw7Z3Ig==?= <per.nordlow gmail.com> writes:
 func(default, default, x, default, default, y)?

 where default is a keyword that substitutes the default value.
Reusing the keyword default here is clever :) I like it.
Apr 07 2014
prev sibling parent reply "w0rp" <devw0rp gmail.com> writes:
On Sunday, 6 April 2014 at 14:47:28 UTC, JN wrote:
 Wouldn't it be better to have named parameters like in Python or


 func(foo=myFoo) or func(bar=myBar) or func(foo=myFoo, bar=myBar)
 and there would be no ambiguity.
Named parameters would be nice to have. I'm not sure how you would implement them in D, though.
Apr 07 2014
parent reply "Frustrated" <Who where.com> writes:
On Monday, 7 April 2014 at 17:46:44 UTC, w0rp wrote:
 On Sunday, 6 April 2014 at 14:47:28 UTC, JN wrote:
 Wouldn't it be better to have named parameters like in Python 
 or


 func(foo=myFoo) or func(bar=myBar) or func(foo=myFoo, 
 bar=myBar)
 and there would be no ambiguity.
Named parameters would be nice to have. I'm not sure how you would implement them in D, though.
it would be relatively easy. void myfunc(name = int x) { } instead of void myfunc(int x) { } then myfunc(name = 4); or one could simply use the variable name void myfunc(int x) { } myfunc(x = 3); Of course assignments may not be valid, one could use := instead. myfunc(x := 3); One could build a template to do it how were but it would require calling the function as a string, e.g., template is passed the call as a string. The template gets the name of the function, looks up the parameter names, parses the arguments and generates the proper call string which is then mixed in. e.g., Named(q{myfunc(x := 3)}); => myfunc(3);
Apr 07 2014
parent reply "Meta" <jared771 gmail.com> writes:
On Monday, 7 April 2014 at 19:47:24 UTC, Frustrated wrote:
 it would be relatively easy.

 void myfunc(name = int x) { }

 instead of

 void myfunc(int x) { }

 then

 myfunc(name = 4);

 or one could simply use the variable name

 void myfunc(int x) { }

 myfunc(x = 3);


 Of course assignments may not be valid, one could use := 
 instead.

 myfunc(x := 3);



 One could build a template to do it how were but it would 
 require calling the function as a string,

 e.g., template is passed the call as a string. The template 
 gets the name of the function, looks up the parameter names, 
 parses the arguments and generates the proper call string which 
 is then mixed in.

 e.g., Named(q{myfunc(x := 3)}); => myfunc(3);
void TestFun(int i, string s = "", bool b) { //... } TestFun(i: 1, b: false); Is there any reason not to use this syntax? It doesn't *seem* to conflict with anything else.
Apr 07 2014
parent reply "Colden Cullen" <ColdenCullen gmail.com> writes:
On Monday, 7 April 2014 at 20:00:39 UTC, Meta wrote:
 On Monday, 7 April 2014 at 19:47:24 UTC, Frustrated wrote:
 it would be relatively easy.

 void myfunc(name = int x) { }

 instead of

 void myfunc(int x) { }

 then

 myfunc(name = 4);

 or one could simply use the variable name

 void myfunc(int x) { }

 myfunc(x = 3);


 Of course assignments may not be valid, one could use := 
 instead.

 myfunc(x := 3);



 One could build a template to do it how were but it would 
 require calling the function as a string,

 e.g., template is passed the call as a string. The template 
 gets the name of the function, looks up the parameter names, 
 parses the arguments and generates the proper call string 
 which is then mixed in.

 e.g., Named(q{myfunc(x := 3)}); => myfunc(3);
void TestFun(int i, string s = "", bool b) { //... } TestFun(i: 1, b: false); Is there any reason not to use this syntax? It doesn't *seem* to conflict with anything else.
I like this a lot more. It looks similar to the associative array literal syntax, which is fitting.
Apr 07 2014
parent "Idan Arye" <GenericNPC gmail.com> writes:
While we're at it - I would also like to see the two forms of 
"variadic" named arguments:

1) Implicit Associative Array Version: works similar to Ruby

void foo(string[string] args...){
     foreach(k,v;args){
         writeln("%s = %s",k,v);
     }
}

foo("a":"b","c":"d");

prints:
a = b
c = d



2) Templated Version: not sure about the declaration syntax. 
Maybe:

void foo(T[...])(T args){
     foreach(field;__traits(allMembers,T)){
         writeln("%s(%s) = 
%s",field,typeof(__traits(getMember,args,field)).stringof,__traits(getMember,args,field));
     }
}

foo(a:1,b:"2");

prints:
a(int) = 1
b(string) = 2
Apr 07 2014
prev sibling parent Byron <byron.heads gmail.com> writes:
On Sat, 05 Apr 2014 21:26:17 -0400, Nick Sabalausky wrote:

 I realize this isn't the time for such a thing to be added to D, but I
 thought I'd put the idea out there, FWIW:
 
 I find myself frequently needing to design APIs that work like this:
 
      func( [optionalFoo], [optionalBar] )
 
 Typically, that's not permitted in C-style languages, including D. They
 only allow this:
 
      func( [optionalFoo, [optionalBar]] )
 
static structs can be initialized by name, maybe that syntax could be expanded upon? void foo( struct{ int a = 1; double b = 3.14; } ) { .. } ... foo( {b: 2} ); that or named tuples with an init value set?
Apr 08 2014