www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - DIP 88: Simple form of named parameters

reply Jacob Carlborg <doob me.com> writes:
This is mostly to prevent ugly hacks like Flag [1].

http://wiki.dlang.org/DIP88

[1] https://dlang.org/phobos/std_typecons.html#.Flag

-- 
/Jacob Carlborg
Jan 23
next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Some comments: 1) Default values You should specify how to use both the `:` syntax and a default parameter value, including an example. They are often going to be used together, and it needs to be clear how they interact. 2) Argument order I think your rule no. 4 is not really necessary, and actually diminishes the usability a lot if there are many name flags. Argument reordering can be done as an independent step before overload resolution (of course this still needs to be aware of all the candidates). But you should add a note that overloading on parameter names alone is illegal (i.e. `void foo(int a:)` vs `void foo(int b:)`). That way, they don't need to affect name mangling. 3) Variadic parameters Is this legal: `void foo(int[] param: ...)`? Or this: `void bar(Args...)(Args args:)`? If yes, what would the calling syntax look like? Also, opDispatch is often used for forwarding arguments. Can parameter names be forwarded, too? If yes, variadic template parameters could capture the names, too, and apply them during expansion as appropriate. 4) Traits Are there going to be traits to inspect the parameter names of a function? Of a TypeTuple/AliasTuple/AliasSeq?
Jan 23
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-23 15:54, Marc Sch├╝tz wrote:

 Some comments:

 1) Default values

 You should specify how to use both the `:` syntax and a default
 parameter value, including an example. They are often going to be used
 together, and it needs to be clear how they interact.
I can add an example.
 2) Argument order

 I think your rule no. 4 is not really necessary, and actually diminishes
 the usability a lot if there are many name flags. Argument reordering
 can be done as an independent step before overload resolution (of course
 this still needs to be aware of all the candidates).
Take this for example: void foo(int a, string b); void foo(string b, int a); void main() { foo(3, "asd"); foo("asd", 3); } The above is legal today. The same example with named arguments: void foo(int a:, string b:); void foo(string b:, int a:); void main() { foo(a: 3, b: "asd"); } What should happen in the above example? Error or which function should be called?
 3) Variadic parameters

 Is this legal: `void foo(int[] param: ...)`? Or this: `void
 bar(Args...)(Args args:)`? If yes, what would the calling syntax look like?
I haven't really though of that. My initial though would be to not allow that for simplicity. I want to keep it fairly simple to have a chance for the proposal to be accepted.
 Also, opDispatch is often used for forwarding arguments. Can parameter
 names be forwarded, too? If yes, variadic template parameters could
 capture the names, too, and apply them during expansion as appropriate.

 4) Traits

 Are there going to be traits to inspect the parameter names of a
 function? Of a TypeTuple/AliasTuple/AliasSeq?
We already have ParameterIdentifierTuple [1]. Are you thinking of something else? I'm not seeing how AliasSeq is related but this PR [2] adds template parameter introspection traits. [1] http://dlang.org/phobos/std_traits.html#ParameterIdentifierTuple [2] https://github.com/D-Programming-Language/dmd/pull/5201 -- /Jacob Carlborg
Jan 23
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Saturday, 23 January 2016 at 16:38:13 UTC, Jacob Carlborg 
wrote:
 void foo(int a:, string b:);
 void foo(string b:, int a:);

 void main()
 {
     foo(a: 3, b: "asd");
 }

 What should happen in the above example? Error or which 
 function should be called?
I hadn't take this into consideration; indeed it would change the way overload resolution works, although it is still possible to handle this (in this case with an error, because it's ambiguous). My thought is that for flags (which was your justification for this feature), you really wouldn't want to care about order, you just want to specify the flags you are interested in and let the others assume their default values. For an example, consider this overload of `spawnProcess` [3]: trusted Pid spawnProcess( in char[][] args, File stdin = std.stdio.stdin, File stdout = std.stdio.stdout, File stderr = std.stdio.stderr, const string[string] env = null, Config config = Config.none, in char[] workDir = null ); Let's say you want to specify the workdir, stdout and a config flag: auto pid = spawnProcess( ["/usr/bin/grep", "-r.", "some string"], workDir: "~/Documents", stdout: fd, config: Config.retainStderr ); If the arguments all must be specified in the right order, it's very hard to get them right without looking them up in the documentation. Besides, if I understand the proposal correctly, you'd actually have to specify all arguments up to the last one used, even though they already have default values.
 We already have ParameterIdentifierTuple [1]. Are you thinking 
 of something else?
ParameterIdentifierTuple doesn't distinguish between parameters that allow your syntax and those that don't.
 I'm not seeing how AliasSeq is related but this PR [2] adds 
 template parameter introspection traits.

 [1] 
 http://dlang.org/phobos/std_traits.html#ParameterIdentifierTuple
 [2] https://github.com/D-Programming-Language/dmd/pull/5201
This was part of my thoughts for variadic template params. Basically, the following should work: int foo(int a:, int b:); int bar(Args...)(Args args:) { return bar(args); } bar(b: 2, a: 1); Currently, `args` is `(2, 1)`. With name parameters, it would become `(b: 2, a: 1)`, i.e. each tuple members gets an (optional) name. There would then need to be a trait for reading these names. [3] http://dlang.org/phobos/std_process.html#.spawnProcess
Jan 23
prev sibling next sibling parent reply tcak <1ltkrs+3wyh1ow7kzn1k sharklasers.com> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Without making things any complex, the simplest thought of mine is: Keep everything same. But allow caller to put name of parameter front its value wherever desired. Let's say, Rect createRect(int x, int y, int width, int height); So you could call it like: createRect( 10, 20, width: 200, height: 500 );
Jan 23
next sibling parent Mint <mintie wute.com> writes:
On Saturday, 23 January 2016 at 15:34:00 UTC, tcak wrote:
 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
 wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Without making things any complex, the simplest thought of mine is: Keep everything same. But allow caller to put name of parameter front its value wherever desired. Let's say, Rect createRect(int x, int y, int width, int height); So you could call it like: createRect( 10, 20, width: 200, height: 500 );
I concur, the additional syntax for declaring functions that take named parameters is entirely extraneous. For the most part, it just complicates implementing the feature by adding additional considerations in regards to how tuples would represent the set of types, ie. ParameterTypeTuple!foo where foo(int a:, int b:) For the most part, I wouldn't be opposed to all functions accepting this syntax, although there still is the consideration of how the call is handled. Notably, is the named-parameter an expression of it's own that is only valid (or only meaningful) in the context of a function or constructor call? Otherwise, we still have the problem of how named-parameters are stored in alias sequences. Also as a minor note, I don't really understand the case for parameter reordering. Typically speaking, the use case for named parameters is to allow calling a function (or constructing and object) without knowledge or concerns for what order the parameters are expected in. Otherwise, the syntax is largely just irrelevant. It simply adds additional cruft without any gain from doing so.
Jan 23
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-01-23 16:34, tcak wrote:

 Without making things any complex, the simplest thought of mine is:

 Keep everything same. But allow caller to put name of parameter front
 its value wherever desired.

 Let's say,

 Rect createRect(int x, int y, int width, int height);

 So you could call it like:

 createRect( 10, 20, width: 200, height: 500 );
I think that the DIP clearly states why it doesn't work like the above. One of the big objections of previous proposals was that the parameters of existing functions would be come part of the API. That is, it would not be possible to rename a parameter without breaking the API. -- /Jacob Carlborg
Jan 23
prev sibling next sibling parent reply Mathias Lang via Digitalmars-d <digitalmars-d puremagic.com> writes:
2016-01-23 15:19 GMT+01:00 Jacob Carlborg via Digitalmars-d <
digitalmars-d puremagic.com>:

 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag

 --
 /Jacob Carlborg
About the rationale:
 Supporting named parameters directly in the language prevents the need to
add workarounds with weird looking syntax like Flag That's going the opposite way of the current trend, which is shift as much feature to library as possible.
 When interfacing with Objective-C it might be desirable to have a method
calling syntax which resembles the syntax used in Objective-C and Swift. Implementing cosmetic features from a language because it appeals to its user doesn't sound that useful to me. The `createReq` example is a bit artificial, as you would usually replace it with a struct. About the design: As mentioned earlier, forcing users to say which arguments can be named and which cannot seems like an unnecessary limit. We'll just end up with everyone using ':' after parameter names (which is very weird looking IMO) in order to be able to use that syntax at the call site, if necessary. Also, bear in mind that a lot of code one will use is not under one's direct control. So if we want to use named parameter with a framework which doesn't define this syntax, you cannot. Since changing the order of parameter is not allowed, it sounds better to just allow this syntax at the call site. Also, one point which was not covered, should the compiler enforce that the name used matches ? E.g. if I use `heigh` instead of `height`, should the compiler tell me "change name `heigh` to `height`" ? Overall, I'm not found of it. It looks like a lot of complication for little to no benefit.
Jan 23
parent Jacob Carlborg <doob me.com> writes:
On 2016-01-23 17:35, Mathias Lang via Digitalmars-d wrote:

 About the rationale:
  > Supporting named parameters directly in the language prevents the
 need to add workarounds with weird looking syntax like Flag

 That's going the opposite way of the current trend, which is shift as
 much feature to library as possible.
Yeah, I know. But I think this is worth adding and that Flag is hurting more than it's helping. Just because something is possible to do in the library doesn't mean it's a good idea. There's been a lot of C++ related features which purely is about mangling. This could be handled in library code together with pragma(mangle). Yet these where added anyway. Yeah, I know everyone will complain that's not the same thing.
  > When interfacing with Objective-C it might be desirable to have a
 method calling syntax which resembles the syntax used in Objective-C and
 Swift.

 Implementing cosmetic features from a language because it appeals to its
 user doesn't sound that useful to me.
This was not part of my initial idea. It was added as a bonus. I can remove it from the rational if it's preferred.
 The `createReq` example is a bit artificial, as you would usually
 replace it with a struct.
Yes.
 About the design:
 As mentioned earlier, forcing users to say which arguments can be named
 and which cannot seems like an unnecessary limit. We'll just end up with
 everyone using ':' after parameter names (which is very weird looking
 IMO) in order to be able to use that syntax at the call site, if
 necessary. Also, bear in mind that a lot of code one will use is not
 under one's direct control. So if we want to use named parameter with a
 framework which doesn't define this syntax, you cannot.
 Since changing the order of parameter is not allowed, it sounds better
 to just allow this syntax at the call site.
I think the DIP clearly states why this is the case. There have been several proposals for named parameters before, they have all been shot down because of this (and the ordering of the parameters). I'm not out to create the most ideal and perfect proposal. I'm trying to provide a proposal that has a chance of being accepted.
 Also, one point which was not covered, should the compiler enforce that
 the name used matches ? E.g. if I use `heigh` instead of `height`,
 should the compiler tell me "change name `heigh` to `height`" ?
Hmm. That's kind of interesting. My idea was that the compiler should enforce that. But if the names don't need to match the only thing that is necessary is a syntax for adding names at the call site. The syntax for declaring the named parameters would not be required.
 Overall, I'm not found of it. It looks like a lot of complication for
 little to no benefit.
How is it complicated? I think it's very simple. The feature could be summarized with this example: Rect createRect(int x:, int y:, int width:, int height:); createRect(x: 0, y: 0, width: 200, height: 200); Just because I described how the feature work in detail doesn't mean it's complicated. -- /Jacob Carlborg
Jan 23
prev sibling next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Sat, 23 Jan 2016 15:19:03 +0100, Jacob Carlborg wrote:

 This is mostly to prevent ugly hacks like Flag [1].
 
 http://wiki.dlang.org/DIP88
Please add proposals to http://wiki.dlang.org/List_of_DIP in the future. (I just did it for you.) There's a DIP category, but nobody includes a descriptive title in their wiki page names, so the auto-generated category name is kind of useless.
 [1] https://dlang.org/phobos/std_typecons.html#.Flag
"Rational" should be "Rationale". One huge usecase for this is methods with many optional parameters. You've missed that. For instance, I wrote a method with six optional parameters recently. It's unusable without named parameters. I switched to a parameter struct, but it's still not that great. I'd also add that this proposal doesn't affect UFCS. What about overloading based on parameter names? The proposal doesn't even mention the idea, so there's no guessing whether you considered and rejected it or just didn't consider it.
 Any parameter that is supposed to be nameable at the call site needs to
 be explicitly declared as such. This is required because otherwise the
 parameters of all exist functions would become part of the API
I'll note that parameter names are already part of documentation, and they're already the only parameter documentation you're guaranteed to have. The Dart devs decided that named parameters must be explicitly set out as named, and they cited the same reason. The C# devs, on the other hand, made every parameter a named parameter. I haven't heard of any explosions in C# land, and it's been five years. Your proposal is closer to C#'s version than Dart's. In Dart, a named parameter cannot be called positionally: foo({a, b}) {} main() { // This works: foo(b: 1, a: 2); // Error: 0 positional arguments expected, 2 found foo(2, 1); } In C#, a named parameter is only about the call site and has nothing to do with the function being called. A function you wrote for C# 1.1 and never modified can be called with named parameters in C# 4.0. You can still call these functions with positional-style parameters.
 Changing the order when using the named parameter syntax is not legal:
Why not? Let's look at languages with named parameters. Python: reorderable. C#: reorderable. Dart: reorderable. Scala: reorderable. Ada: reorderable. Given this trend, people will be confused if they can't reorder named parameters. The main benefit of not letting people reorder things is reducing the amount of work *you* need to do when creating this DIP (and the amount others need to do when implementing it). To sum up, your proposal is approximately the same as adding a comment by each parameter saying which parameter it is. A bit nicer because it's got lighter syntax, a lot less usable because the function has to opt in. Document how it's supposed to handle optional parameters and allow reordering, and then we're getting somewhere.
Jan 23
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-23 19:27, Chris Wright wrote:

 One huge usecase for this is methods with many optional parameters.
 You've missed that. For instance, I wrote a method with six optional
 parameters recently. It's unusable without named parameters. I switched
 to a parameter struct, but it's still not that great.
I did not miss that. I deliberately chose to not support changing the order of the arguments.
 I'd also add that this proposal doesn't affect UFCS.
How would it affect UFCS?
 What about overloading based on parameter names? The proposal doesn't
 even mention the idea, so there's no guessing whether you considered and
 rejected it or just didn't consider it.
The DIP mentions that no change is required for function overloading. But I added an explicitly rule about that.
 I'll note that parameter names are already part of documentation, and
 they're already the only parameter documentation you're guaranteed to
 have.
Currently it's possible to change a parameter name without the call site needing any change. Documentation has nothing to do with that.
 The Dart devs decided that named parameters must be explicitly set out as
 named, and they cited the same reason. The C# devs, on the other hand,
 made every parameter a named parameter. I haven't heard of any explosions
 in C# land, and it's been five years.

 Your proposal is closer to C#'s version than Dart's. In Dart, a named
 parameter cannot be called positionally:

    foo({a, b}) {}
    main() {
      // This works:
      foo(b: 1, a: 2);

      // Error: 0 positional arguments expected, 2 found
      foo(2, 1);
    }

 In C#, a named parameter is only about the call site and has nothing to
 do with the function being called. A function you wrote for C# 1.1 and
 never modified can be called with named parameters in C# 4.0. You can
 still call these functions with positional-style parameters.

 Changing the order when using the named parameter syntax is not legal:
Why not? Let's look at languages with named parameters. Python: reorderable. C#: reorderable. Dart: reorderable. Scala: reorderable. Ada: reorderable. Given this trend, people will be confused if they can't reorder named parameters. The main benefit of not letting people reorder things is reducing the amount of work *you* need to do when creating this DIP (and the amount others need to do when implementing it).
No. The only reason why that is not allowed is to have a greater chance of the DIP being accepted. The language would be even more complicated if it was possible to reorder the arguments. It's a benefit for the users if the language is simpler.
 Document how it's supposed to handle optional parameters
It's already documented. -- /Jacob Carlborg
Jan 24
parent reply Chris Wright <dhasenan gmail.com> writes:
On Sun, 24 Jan 2016 13:04:20 +0100, Jacob Carlborg wrote:
 On 2016-01-23 19:27, Chris Wright wrote:
 I'd also add that this proposal doesn't affect UFCS.
How would it affect UFCS?
It shouldn't. However, it is another way to pass function arguments, so for thoroughness it would be better to mention it.
 I'll note that parameter names are already part of documentation, and
 they're already the only parameter documentation you're guaranteed to
 have.
Currently it's possible to change a parameter name without the call site needing any change. Documentation has nothing to do with that.
I mentioned this because it strongly encourages people to use appropriate and descriptive names when writing functions. Appropriate and descriptive names are less likely to need to change. This doesn't eliminate your point that, today, people can change argument names and be guaranteed not to break client code.
 Given this trend, people will be confused if they can't reorder named
 parameters. The main benefit of not letting people reorder things is
 reducing the amount of work *you* need to do when creating this DIP
 (and the amount others need to do when implementing it).
No. The only reason why that is not allowed is to have a greater chance of the DIP being accepted. The language would be even more complicated if it was possible to reorder the arguments. It's a benefit for the users if the language is simpler.
There's more chance of it being rejected as not sufficiently beneficial and less chance of it being rejected as too much complexity. But let's look at that complexity angle. What sort of complexity do we want to eliminate? There was recently a post that made the point that an hour of time from a compiler developer making something easier and saving each user an hour of time is a huge productivity gain in all. So we want to reduce complexity for language users, and making a more complex compiler is only problematic insofar as it makes the compiler slower or buggier. All else being equal, adding constraints generally adds complexity and removing them generally reduces complexity. Any constraint we add has to pay for its complexity somehow. Having to remember parameter order while specifying unambiguously which parameter I'm trying to use is an added constraint and therefore added complexity. It gives nothing in return. Since absolutely no language has previously required named arguments to retain the same order they're declared in, apparently everyone else disagrees that the feature is too complex. Even Python, which is specifically aimed at beginners, allows reordering. The most similar thing to named parameters that's already in D is struct initializers. These do not require that you order the struct member initializers in the same order as the related fields are declared. This means that users have to remember two sets of similar syntax that function differently in an important way. That's added complexity within D. It's a benefit to users if things work in a familiar way. I haven't been able to find a language that works in the way you propose, so for people familiar with other languages, this DIP results in confusing behavior. That's added complexity across languages. And since D doesn't have the same market penetration that Java or Javascript have, it's extremely difficult for a person to be familiar only with D.
 Document how it's supposed to handle optional parameters
It's already documented.
I missed that. My apologies. Your proposal says this works: void foo(int a: = 3, int b: = 4); foo(); foo(a: 5, b: 6); But we can conclude that the following is not allowed: foo(b: 6); This is because it's not legal to change the order of arguments by specifying names, and you must supply optional parameters of the same type in order, without omitting any, if you don't have names. Your proposal did not involve any special casing here. Is this what you intended? If so, please document it so other reviewers don't have to wonder.
Jan 24
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-24 19:23, Chris Wright wrote:

 It shouldn't. However, it is another way to pass function arguments, so
 for thoroughness it would be better to mention it.
Added an example.
 Is this what you intended? If so, please document it so other reviewers
 don't have to wonder.
Added an example. -- /Jacob Carlborg
Jan 25
parent reply arturg <var.spool.mail700 gmail.com> writes:
On Monday, 25 January 2016 at 08:55:55 UTC, Jacob Carlborg wrote:
 On 2016-01-24 19:23, Chris Wright wrote:

 It shouldn't. However, it is another way to pass function 
 arguments, so
 for thoroughness it would be better to mention it.
Added an example.
 Is this what you intended? If so, please document it so other 
 reviewers
 don't have to wonder.
Added an example.
hi, named args is mainly a feature for the caller, is it really necessary to add a new syntax to the function definition? besides making some args part of the api and some not. and if you cant omit args with default initializer like in your example void foo(int a: = 3, int b: = 4); foo(); foo(a: 5, b: 6); foo(b: 6); // this is not allowed since it's not legal to reorder the arguments this feature is nothing more then a comment and the simplest way to implement this, is to add a single word comment type which might be usefull in more situations.
Jan 25
parent reply Chris Wright <dhasenan gmail.com> writes:
On Mon, 25 Jan 2016 12:56:01 +0000, arturg wrote:

 On Monday, 25 January 2016 at 08:55:55 UTC, Jacob Carlborg wrote:
 On 2016-01-24 19:23, Chris Wright wrote:

 It shouldn't. However, it is another way to pass function arguments,
 so for thoroughness it would be better to mention it.
Added an example.
 Is this what you intended? If so, please document it so other
 reviewers don't have to wonder.
Added an example.
hi, named args is mainly a feature for the caller, is it really necessary to add a new syntax to the function definition? besides making some args part of the api and some not.
Python, C#, Scala, and Ada do not require any special annotations at the function definition to use named arguments. Dart and Ruby do. Obviously it's not necessary, but there is precedent both ways.
 this feature is nothing more then a comment and the simplest way to
 implement this, is to add a single word comment type which might be
 usefull in more situations.
It's a comment that the compiler verifies. You could argue that the bulk of the type system is comments that the compiler verifies.
Jan 25
parent reply arturg <var.spool.mail700 gmail.com> writes:
On Monday, 25 January 2016 at 16:17:27 UTC, Chris Wright wrote:

 It's a comment that the compiler verifies. You could argue that 
 the bulk of the type system is comments that the compiler 
 verifies.
named arguments solve this problem void foo(int a = 4, int b = 10, int c = 3) {} foo(c : 5); beeing callside documentation is only a byproduct.
Jan 25
parent jmh530 <john.michael.hall gmail.com> writes:
On Monday, 25 January 2016 at 16:38:19 UTC, arturg wrote:
 named arguments solve this problem

 void foo(int a = 4, int b = 10, int c = 3) {}

 foo(c : 5);

 beeing callside documentation is only a byproduct.
I wouldn't classify that as a problem per se, more of an inconvenience. And a bigger inconvenience the longer the list of default arguments.
Jan 25
prev sibling next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
I already see confusing code like: foo(10, 20, width: ((big) ? 600 : 200), height: ((big) ? 800 : 480)); I know many people, especially those who are coming from the Python world, want named parameters, but I am not entirely sure we want that in D. I would rather use the Builder pattern instead, because that typically what people do with named parameters...
Jan 23
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
To be pedantic, this would be adding named arguments, not named parameters. The parameters are already named, but mixing up arguments and parameters is a common mistake to make, and folks do frequently talk about named parameters with regards to this sort of feature. Regardless, I for one, hope that we never have named arguments. It's just one more thing that's then part of the function's signature that you can't change without breaking code and will cause that much more bikeshedding. At least right now, parameter names don't matter much, whereas if they're part of the API, then they're open to all of the issues that function naming has. And while I appreciate that this DIP does not make named arguments the default behavior for a function, making it up to the library writer whether they support named arguments or not is likely to create a lot of bikeshedding and debates over whether a particular function or library should support named arguments. Personally, I'd never use them under any circumstances. However, I do appreciate that the DIP doesn't allow for reordering the arguments based on their names. It does make named arguments a bit less ugly. - Jonathan M Davis
Jan 23
next sibling parent reply default0 <Kevin.Labschek gmx.de> writes:
On Sunday, 24 January 2016 at 02:51:43 UTC, Jonathan M Davis 
wrote:
 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
 wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Regardless, I for one, hope that we never have named arguments. It's just one more thing that's then part of the function's signature that you can't change without breaking code and will cause that much more bikeshedding. At least right now, parameter names don't matter much, whereas if they're part of the API, then they're open to all of the issues that function naming has. And while I appreciate that this DIP does not make named arguments the default behavior for a function, making it up to the library writer whether they support named arguments or not is likely to create a lot of bikeshedding and debates over whether a particular function or library should support named arguments. Personally, I'd never use them under any circumstances. - Jonathan M Davis
So given this method: void M(int a, int b = 1, int c = 2, int d = 3, int e = 4, int f = 5, int g = 6, int h = 7, int i = 8) { } You prefer calling it this way: M(5, 1, 2, 3, 4, 5, 6, 7, 23); As opposed to: M(5, i: 23); Or am I misunderstanding you? Also consider that the two aren't even the same, if the library author decides to change some default values you will likely want to update the former call, but the latter can stay the same. Less of a maintenance burden. Coming from C# where it's a free-for-all-every-argument-can-be-named implementation, I never once even heard someone complain or experience breakage because some parameter name changed, which makes me think that the possibility of breakage, while of course real, seems to not be an issue in practice. Judging from my own coding habits in C#, I rarely change parameter names anyways (about as rarely as I change method names) and I rarely use named arguments anyways (most functions I dealt with neither had many optional parameters, nor had "blind" parameters where the documentation would've been sincerely helpful). Also consider that using named arguments is a decision made by the caller ("I want to document these parameters..." or "Ugh I don't want to pass 10.000 arguments to this function, SKIP!"), not the callee ("Will the user want to document these parameters? Hmm... it's possible!" or "Will the user want to skip these default values? Hmm there's at most three here, not sure if it's worthwhile..."). As for this DIP I don't know - it seems to be well-thought-out, albeit with a focus on ease of implementation rather than usefulness for a language user, so I don't really have a conclusive opinion on that.
Jan 24
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, 24 January 2016 at 10:14:53 UTC, default0 wrote:
 On Sunday, 24 January 2016 at 02:51:43 UTC, Jonathan M Davis 
 wrote:
 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
 wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Regardless, I for one, hope that we never have named arguments. It's just one more thing that's then part of the function's signature that you can't change without breaking code and will cause that much more bikeshedding. At least right now, parameter names don't matter much, whereas if they're part of the API, then they're open to all of the issues that function naming has. And while I appreciate that this DIP does not make named arguments the default behavior for a function, making it up to the library writer whether they support named arguments or not is likely to create a lot of bikeshedding and debates over whether a particular function or library should support named arguments. Personally, I'd never use them under any circumstances. - Jonathan M Davis
So given this method: void M(int a, int b = 1, int c = 2, int d = 3, int e = 4, int f = 5, int g = 6, int h = 7, int i = 8) { } You prefer calling it this way: M(5, 1, 2, 3, 4, 5, 6, 7, 23); As opposed to: M(5, i: 23);
Anyone who wrote a function like that is just plain writing bad code. With that many parameters, they should be creating a struct to hold the values. Named arguments really only seem to help in cases where functions are badly designed. I don't want to use them in my code, and I don't want to have to deal with them in other people's code. And as a library writer, I don't want to have to argue with anyone about whether the names that I picked for my parameters were good or not. We already get way too much bikeshedding over function names as it is without adding parameter names into the mix. I would strongly argue that anyone who feels the need for named parameters should rethink how they're designing their functions. - Jonathan M Davis
Jan 24
next sibling parent Andrej Mitrovic via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 1/24/16, Jonathan M Davis via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 I would strongly argue that anyone who feels the need for named
 parameters should rethink how they're designing their functions.
Another example where they become useful is with functions that take source and destination parameters. E.g.: void copy ( Buffer src, Buffer tgt ) { } vs void copy ( Buffer tgt, Buffer src ) { } With regard to param names being part of the API I think that: - Parameter names very rarely change - It's the responsibility of the client code authors to fix any code breakage due to parameter renaming I think the best thing we can do before completely dismissing this idea is to look into the experience of other programming communities (like Python) and try to weigh the pros vs the cons.
Jan 24
prev sibling parent default0 <Kevin.Labschek gmx.de> writes:
On Sunday, 24 January 2016 at 10:40:10 UTC, Jonathan M Davis 
wrote:
 On Sunday, 24 January 2016 at 10:14:53 UTC, default0 wrote:

 Anyone who wrote a function like that is just plain writing bad 
 code. With that many parameters, they should be creating a 
 struct to hold the values. Named arguments really only seem to 
 help in cases where functions are badly designed. I don't want 
 to use them in my code, and I don't want to have to deal with 
 them in other people's code. And as a library writer, I don't 
 want to have to argue with anyone about whether the names that 
 I picked for my parameters were good or not. We already get way 
 too much bikeshedding over function names as it is without 
 adding parameter names into the mix.

 - Jonathan M Davis
I agree that functions like that are badly designed. However they exist and you can't always change them. That said, if there'd be traits for parameter names you can probably automate the creation of a decent struct-wrapper, making this a non-issue for D (you could not do that in C#, so writing wrappers would be a huge pain there). Regarding the bikeshedding: I honestly don't think this increases the amount of bikeshedding happening. There'll always be bikeshedding anyways, independent of if you give many opportunities for it or not. Thinking further about this, though, and being the inexperienced D user I am, I assume you use struct wrappers sort of like this? ArgStruct s; s.param1 = 12; M(s); If yes, consider that a named argument alternative would be far less noisy: M(param1: 12); If my code above is far more verbose than what you'd usually do (ie comparable to the succinctness of the named argument), can you please give an example?
Jan 24
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-01-24 11:14, default0 wrote:

 As for this DIP I don't know - it seems to be well-thought-out, albeit
 with a focus on ease of implementation rather than usefulness for a
 language user, so I don't really have a conclusive opinion on that.
It's focused on having a realistic chance of being accepted. -- /Jacob Carlborg
Jan 24
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Sun, 24 Jan 2016 10:14:53 +0000, default0 wrote:
 So given this method:
 void M(int a, int b = 1, int c = 2, int d = 3, int e = 4, int f =
 5, int g = 6, int h = 7, int i = 8)
 {
 }
 
 You prefer calling it this way:
 M(5, 1, 2, 3, 4, 5, 6, 7, 23);
 As opposed to:
 M(5, i: 23);
Actually, the proposal doesn't allow the latter. It explicitly doesn't allow you to change the order of parameters and says nothing about allowing this as a special case, so you would have to write it as: M(5, b: 1, c: 2, d: 3, e: 4, f: 5, g: 6, h: 7, i: 23); Which is slightly more readable but still a nightmare for maintainability.
Jan 24
prev sibling parent Ola Fosheim =?UTF-8?B?R3LDuHN0YWQ=?= writes:
On Sunday, 24 January 2016 at 02:51:43 UTC, Jonathan M Davis 
wrote:
 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
 wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
To be pedantic, this would be adding named arguments, not named parameters. The parameters are already named, but mixing up arguments and parameters is a common mistake to make, and folks do frequently talk about named parameters with regards to this sort of feature.
To be even more pedantic it is usually called formal and active parameters: void f(int a) // formal parameter f(3) // actual parameter
Jan 25
prev sibling next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Someone on another thread made a comment that it wasn't clear what : does in D. I suspect this won't help. I'm more used to equals from R than : in C# and Swift, though I'm not sure that really matters much. There is a good discussion of named parameters here (particularly amon's answer): http://programmers.stackexchange.com/questions/219593/why-do-many-languages-not-support-named-parameters
Jan 23
prev sibling next sibling parent reply Gary Willoughby <dev nomad.so> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88
"A new syntax is added to be used when declaring a function that should be callable with named parameters:" Please, no more new syntax! This can be done using templates and Flag is not an ugly hack!
Jan 24
next sibling parent default0 <Kevin.Labschek gmx.de> writes:
On Sunday, 24 January 2016 at 11:20:20 UTC, Gary Willoughby wrote:
 Please, no more new syntax!

 This can be done using templates and Flag is not an ugly hack!
Why no more new syntax? I agree that keeping the language simple is a good idea, but new syntax isn't usually hard to learn, especially if it hits the sweet spot between being different enough to easily tell that it means something special/new and being close enough to the rest of the syntax to not feel alien. Also Flag seems like quite the waste of linespace. In addition to the name of the parameter I lose 7 characters (Flag!""). Named arguments would only use up 2 (: ) or 3 ( = ) characters. Plus, being a library solution, at first it makes you wonder if there is something special going on with Flag (cuz you know, types usually have associated behaviour), instead of being purely for documentation purposes. Totally qualifies as a hack to me.
Jan 24
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-01-24 12:20, Gary Willoughby wrote:

 This can be done using templates and Flag is not an ugly hack!
It's one of the most ugly things I've seen in D. -- /Jacob Carlborg
Jan 24
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Sun, 24 Jan 2016 11:20:20 +0000, Gary Willoughby wrote:

 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88
"A new syntax is added to be used when declaring a function that should be callable with named parameters:" Please, no more new syntax! This can be done using templates and Flag is not an ugly hack!
Okay, but only part of this has to do with booleans. Taking your proposal to its logical conclusion, you're saying I should write: alias Typedef!(string, "key") Key; alias Typedef!(string, "startDocumentID") StartDocumentID; alias Typedef!(ulong, "limit") Limit; alias Typedef!(ulong, "resultsPerPage") ResultsPerPage; CouchPaginatedRange query( Key key = Key(""), StartDocumentID startID = StartDocumentID(""), Limit limit = Limit(ulong.max), ResultsPerPage resultsPerPage = ResultsPerPage(50), Flag!"includeDocuments" includeDocuments = No.includeDocuments) {} query(Key("foo"), Yes.includeDocuments); This is an interesting proposal, but I'll only ever consider it if Walter says we're never getting sparse named arguments.
Jan 24
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.ca> writes:
On 2016-01-23 14:19:03 +0000, Jacob Carlborg <doob me.com> said:

 This is mostly to prevent ugly hacks like Flag [1].
 
 http://wiki.dlang.org/DIP88
 
 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Interesting. This is somewhat similar to an experiment of mine from 5 years ago. My implementation was a bit of a hack, but my intent was to have a proof of concept done quickly. All arguments were considered optionally named in this experiment (there was no syntax to enable this in the function prototype). Also it didn't extend to templates arguments. You can my discussion with Walter about it here: https://github.com/michelf/dmd/commit/673bae4982ff18a3d216bc1578f50d40f4d26d7a Mixing reordering with overloading can become quite complicated. It's good that you leave that out at first. Adding reordering should be left as a separate task for later, if desired. Small steps. Have you considered supporting separate variable names? Like this: void login(string username: name, string password:) { writeln(name ~ ": " ~ password); } where an identifier following the colon, if present, would be the name of the variable inside the function. That way you can use a shorter variable when it makes sense to do so. Or change the variable name without affecting the API. -- Michel Fortin http://michelf.ca
Jan 24
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-01-24 14:16, Michel Fortin wrote:

 Have you considered supporting separate variable names? Like this:

      void login(string username: name, string password:) {
          writeln(name ~ ": " ~ password);
      }
No, not really. -- /Jacob Carlborg
Jan 24
prev sibling parent Chris Wright <dhasenan gmail.com> writes:
On Sun, 24 Jan 2016 08:16:35 -0500, Michel Fortin wrote:

 Have you considered supporting separate variable names? Like this:
 
 	void login(string username: name, string password:) {
 		writeln(name ~ ": " ~ password);
 	}
You mean: void login(string username:, string password:) { alias name = username; writeln(name, ": ", password); }
Jan 24
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.ca> writes:
On 2016-01-23 14:19:03 +0000, Jacob Carlborg <doob me.com> said:

 This is mostly to prevent ugly hacks like Flag [1].
 
 http://wiki.dlang.org/DIP88
 
 [1] https://dlang.org/phobos/std_typecons.html#.Flag
On further thought, how do you make templates with specialization take named arguments? template TFoo(T) { ... } // #1 template TFoo(T : T[]) { ... } // #2 template TFoo(T : char) { ... } // #3 My guess would be this: template TFoo(T:) { ... } // #1 template TFoo(T: : T[]) { ... } // #2 template TFoo(T: : char) { ... } // #3 ... but it makes the declaration a bit strange. -- Michel Fortin http://michelf.ca
Jan 24
parent reply Jacob Carlborg <doob me.com> writes:
On 2016-01-24 14:24, Michel Fortin wrote:

 On further thought, how do you make templates with specialization take
 named arguments?

      template TFoo(T)        { ... } // #1
      template TFoo(T : T[])  { ... } // #2
      template TFoo(T : char) { ... } // #3

 My guess would be this:

      template TFoo(T:)        { ... } // #1
      template TFoo(T: : T[])  { ... } // #2
      template TFoo(T: : char) { ... } // #3

 ... but it makes the declaration a bit strange.
Yes. Rule 7 covers that. There's also an example showing the syntax, search for "Template specialization with named parameter". Yes, it does look a bit strange. -- /Jacob Carlborg
Jan 24
parent reply xenon325 <anm programmer.net> writes:
On Sunday, 24 January 2016 at 13:33:50 UTC, Jacob Carlborg wrote:
 On 2016-01-24 14:24, Michel Fortin wrote:

 On further thought, how do you make templates with 
 specialization take
 named arguments?

      template TFoo(T)        { ... } // #1
      template TFoo(T : T[])  { ... } // #2
      template TFoo(T : char) { ... } // #3

 My guess would be this:

      template TFoo(T:)        { ... } // #1
      template TFoo(T: : T[])  { ... } // #2
      template TFoo(T: : char) { ... } // #3

 ... but it makes the declaration a bit strange.
Yes. Rule 7 covers that. There's also an example showing the syntax, search for "Template specialization with named parameter". Yes, it does look a bit strange.
Just like C++98 used to require space between two `>` in templates ? ;) I see no way Walter would accept that. Why not just: ``` namedParams template TFoo(T : char) ``` Other thoughts: 1. How often would one need to mix named and unnamed parameters ? 2. And how often would one need named params at all, so dlang should provide super-easy syntax with inline `:` ? 3. `:` is not very visible, nor searchable. (OTOH, with a lot of a)
Jan 25
next sibling parent xenon325 <anm programmer.net> writes:
On Tuesday, 26 January 2016 at 07:05:53 UTC, xenon325 wrote:
 3. `:` is not very visible, nor searchable. (OTOH, with a lot 
 of a)
(has lost part of the message) OTOH, with a lots of annotations ` namedParams` could be not very visible as well
Jan 25
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 01/26/2016 08:05 AM, xenon325 wrote:
 Yes. Rule 7 covers that. There's also an example showing the syntax,
 search for "Template specialization with named parameter". Yes, it
 does look a bit strange.
Just like C++98 used to require space
:: is not a token, so no space is required.
 between two `>` in templates ? ;)
 I see no way Walter would accept that.
https://issues.dlang.org/show_bug.cgi?id=6589
Jan 27
parent xenon325 <anm programmer.net> writes:
On Thursday, 28 January 2016 at 03:00:35 UTC, Timon Gehr wrote:
 Just like C++98 used to require space
:: is not a token, so no space is required.
 between two `>` in templates ? ;)
 I see no way Walter would accept that.
https://issues.dlang.org/show_bug.cgi?id=6589
Hm, you are right. That was incorrect analogy.
Jan 28
prev sibling next sibling parent reply HaraldZealot <harald_zealot tut.by> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Why not simply allow programmer to declare auto variable at function call side like in `foreach`? E.g. ```d void foo(int width, int height){...} //function definition foo(width = 2, height = 3); // function call ```
Jan 25
parent reply arturg <var.spool.mail700 gmail.com> writes:
On Monday, 25 January 2016 at 13:40:18 UTC, HaraldZealot wrote:
 On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
 wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
Why not simply allow programmer to declare auto variable at function call side like in `foreach`? E.g. ```d void foo(int width, int height){...} //function definition foo(width = 2, height = 3); // function call ```
the equal sign cant be used because D behaves like this int width, height; foo(width = 5, height = 10); // 5 // 10 width.writeln; // 5 height.writeln; // 10 void foo(int width, int height) { width.writeln; height.writeln; } and in python def foo(width, height): print(width) print(height) width = 0 height = 0 foo(width = 5, height = 10) # 5 # 10 print(width) # 0 print(height) # 0
Jan 25
parent reply HaraldZealot <harald_zealot tut.by> writes:
On Monday, 25 January 2016 at 14:35:09 UTC, arturg wrote:
 On Monday, 25 January 2016 at 13:40:18 UTC, HaraldZealot wrote:
 the equal sign cant be used because D behaves like this

 int width, height;

 foo(width = 5, height = 10); // 5
                              // 10
 width.writeln;  // 5
 height.writeln; // 10

 void foo(int width, int height)
 {
     width.writeln;
     height.writeln;
 }
You didn't pay attention, that in my proposal `foo(width = 5, height = 10);` width and height were declared in place of use, and scope is only this call
Jan 25
parent arturg <var.spool.mail700 gmail.com> writes:
On Monday, 25 January 2016 at 16:50:49 UTC, HaraldZealot wrote:
 On Monday, 25 January 2016 at 14:35:09 UTC, arturg wrote:
 On Monday, 25 January 2016 at 13:40:18 UTC, HaraldZealot wrote:
 the equal sign cant be used because D behaves like this

 int width, height;

 foo(width = 5, height = 10); // 5
                              // 10
 width.writeln;  // 5
 height.writeln; // 10

 void foo(int width, int height)
 {
     width.writeln;
     height.writeln;
 }
You didn't pay attention, that in my proposal `foo(width = 5, height = 10);` width and height were declared in place of use, and scope is only this call
this code compiles right now which would make it a breaking change if you use the = and change the current behavior module foo; int width; int height; module app; import foo; void bar(int width, int height) {} void main() { bar(width = 5, height = 10); }
Jan 25
prev sibling parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
On Saturday, 23 January 2016 at 14:19:03 UTC, Jacob Carlborg 
wrote:
 This is mostly to prevent ugly hacks like Flag [1].

 http://wiki.dlang.org/DIP88

 [1] https://dlang.org/phobos/std_typecons.html#.Flag
I agree Flag is a bit ugly to use, but IIUC this proposal does not enforce the use of a named argument, it's optional. Flag does call site name enforcement. Also, named template arguments is an interesting idea, but they don't seem to be so useful as named function arguments. Boolean function arguments are pretty common, but I'm not sure templates have an equivalent compelling example. They could be left out at first perhaps.
Jan 25