www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - named arguments (C++) - Something D could learn from

reply bauss <jj_1337 live.dk> writes:
In C++ you can achieve named arguments using templates and 
operator overload:

It's sad that D is still against having named arguments, but it's 
possible in something that D thrives to be better than.

https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/

I assume this is not possible to port to D because D doesn't do 
operator overload in the same way C++ does, correct?

I still think named arguments should be something to consider 
implementing for D, but since everything requires a DIP and it's 
very hard to write a DIP that's proper for such a thing then I 
guess it'll just be a dream, forever.
Dec 14 2018
next sibling parent JN <666total wp.pl> writes:
On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and 
 it's very hard to write a DIP that's proper for such a thing 
 then I guess it'll just be a dream, forever.
Even if a DIP would happen, people would just say it's unnecessary/bloat/can be done as a library function through some ugly mixin :/
Dec 14 2018
prev sibling next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 12/14/18 3:13 PM, bauss wrote:
 In C++ you can achieve named arguments using templates and operator 
 overload:
 
 It's sad that D is still against having named arguments, but it's 
 possible in something that D thrives to be better than.
 
 https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/
 
 I assume this is not possible to port to D because D doesn't do operator 
 overload in the same way C++ does, correct?
Bleh, C++ is so ugly. I can't see how D can't do better. operator overloading works fine in D, just like it does in the given example. What C++ allows is: 1. operator overloading of arbitrary operators. D has certain requirements for certain operators. 2. extracting the operator overloading to a module-level function. D requires operator overloading inside a type. But neither of those problems are in play here. You could practically port the solution exactly to D. HOWEVER, I don't think the mechanics make this solution worthwhile. Carrying around extra structs and wrapping things in this complicated way doesn't seem very attractive to me. -Steve
Dec 14 2018
prev sibling next sibling parent reply Dukc <ajieskola gmail.com> writes:
On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and 
 it's very hard to write a DIP that's proper for such a thing 
 then I guess it'll just be a dream, forever.
Hope if far from lost here. There is not one, but two DIPs in draft stage to add named function parameters to D: https://github.com/dlang/DIPs/pull/123 https://github.com/dlang/DIPs/pull/126
Dec 14 2018
next sibling parent rikki cattermole <rikki cattermole.co.nz> writes:
On 15/12/2018 10:22 AM, Dukc wrote:
 On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and it's very 
 hard to write a DIP that's proper for such a thing then I guess it'll 
 just be a dream, forever.
Hope if far from lost here. There is not one, but two DIPs in draft stage to add named function parameters to D: https://github.com/dlang/DIPs/pull/123 https://github.com/dlang/DIPs/pull/126
Disclaimer the second DIP is mine. The first is mostly concerned with syntax sugar. The second is about modelling externally setting internal (but private) attributes that are visible publicly. They both have their own pluses and minuses, but IMO the second is much harder to implement as a library. In fact we already do, see ElementType for input ranges. We can do better at modelling this.
Dec 14 2018
prev sibling parent John Chapman <johnch_atms hotmail.com> writes:
On Friday, 14 December 2018 at 21:22:38 UTC, Dukc wrote:
 On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and 
 it's very hard to write a DIP that's proper for such a thing 
 then I guess it'll just be a dream, forever.
Hope if far from lost here. There is not one, but two DIPs in draft stage to add named function parameters to D: https://github.com/dlang/DIPs/pull/123 https://github.com/dlang/DIPs/pull/126
If anyone wants to play with an implementation, I've updated Jacob Carlborg's old work here: https://github.com/jsatellite/dmd/tree/named-arguments
Dec 14 2018
prev sibling next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Fri, 14 Dec 2018 20:13:42 +0000, bauss wrote:
 In C++ you can achieve named arguments using templates and operator
 overload:
 
 It's sad that D is still against having named arguments, but it's
 possible in something that D thrives to be better than.
 
 https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/
 
 I assume this is not possible to port to D because D doesn't do operator
 overload in the same way C++ does, correct?
 
 I still think named arguments should be something to consider
 implementing for D, but since everything requires a DIP and it's very
 hard to write a DIP that's proper for such a thing then I guess it'll
 just be a dream, forever.
The only vaguely interesting thing in that example is selecting an item based on its compile-time type, and that's easy. In addition to what the FluentCpp post supports, we'd like to add default value handling, and we'd like to automatically generate the forwarding function. I hacked this up: https://git.ikeran.org/dhasenan/snippets/src/branch/master/namedargs/ forwarding.d And you use it like: --- writeln(dispatch!greet(Count(3), Name("Aerith"))); --- Issues: * You have to write a bunch of extra structs. Namespace pollution ahoy. Code that looks right but isn't because both module A and module B define a `Name` parameter struct. You can probably reduce that a bit with std.typecons.Typedef. * This ignores superfluous arguments. That's left as an exercise to the reader. * Documentation. You can document the non-dispatch version of things and suggest that users use the dispatch wrapper, or you can document an alias around dispatch!fun and have no documentation for what parameters the function has. * Overloads. It is difficult to refer to a specific overload from a set, and if you want to provide aliases for the dispatch function, it is difficult to expose all of them. (On the other hand, with optional, reorderable, named arguments, you can much more easily pack things into the same function, assuming they're all supposed to do roughly the same thing, which is good design.) * Ref and out parameters. You can probably support them, but it would be kind of hard. Another strategy is to automatically generate a struct for each function, then use the struct initialization syntax to create the value and call a method on it to invoke the function. It would work like: --- Invoker!greet f = { name: "Rydia", count: 1 }; writeln(f()); --- And that has its own issues: you have to declare a new variable each time, it's easy to call the function multiple times, and ref and out parameters are much more awkward. You can at least tell whether a parameter was provided or not using std.typecons.Nullable or the like.
Dec 14 2018
parent reply NX <nightmarex1337 hotmail.com> writes:
On Friday, 14 December 2018 at 23:10:35 UTC, Neia Neutuladh wrote:
 I hacked this up:

 https://git.ikeran.org/dhasenan/snippets/src/branch/master/namedargs/forwarding.d
Check this out: https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
Dec 15 2018
parent Neia Neutuladh <neia ikeran.org> writes:
On Sat, 15 Dec 2018 15:59:48 +0000, NX wrote:
 On Friday, 14 December 2018 at 23:10:35 UTC, Neia Neutuladh wrote:
 I hacked this up:

 https://git.ikeran.org/dhasenan/snippets/src/branch/master/namedargs/
forwarding.d
 
 Check this out:
 https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
Nice! I think I saw this before and forgot about it. I'll still probably never use it, but oh well.
Dec 15 2018
prev sibling next sibling parent Jacob Carlborg <doob me.com> writes:
On 2018-12-14 21:13, bauss wrote:
 In C++ you can achieve named arguments using templates and operator 
 overload:
 
 It's sad that D is still against having named arguments, but it's 
 possible in something that D thrives to be better than.
 
 https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/
 
 I assume this is not possible to port to D because D doesn't do operator 
 overload in the same way C++ does, correct?
 
 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and it's very 
 hard to write a DIP that's proper for such a thing then I guess it'll 
 just be a dream, forever.
Here's a simple version in D that doesn't require to declare a variable for each name, does not support reordering names: void foo(int a) { writeln(a); } void bar(NamedArgument!("b", int) b) { writeln(b); } struct NamedArgument(string name, T) { T value; alias value this; } struct NamedArgumentProxy { auto opDispatch(string name, T)(T value) const { return NamedArgument!(name, T)(value); } } immutable NamedArgumentProxy na; void main() { foo(na.a = 3); // the name "a" here doesn't matter, it can be anything foo(na.b = 4); // this has to be "b", otherwise it won't compile } https://run.dlang.io/is/zgejqi -- /Jacob Carlborg
Dec 16 2018
prev sibling next sibling parent reply Matthew OConnor <thegreendragon gmail.com> writes:
On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 In C++ you can achieve named arguments using templates and 
 operator overload:

 It's sad that D is still against having named arguments, but 
 it's possible in something that D thrives to be better than.

 https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/

 I assume this is not possible to port to D because D doesn't do 
 operator overload in the same way C++ does, correct?

 I still think named arguments should be something to consider 
 implementing for D, but since everything requires a DIP and 
 it's very hard to write a DIP that's proper for such a thing 
 then I guess it'll just be a dream, forever.
I am a C++ developer by day and reading that article made me cringe. C++ has this creeping, expansive, everything ends up in the language or writable in it, if you accept awful messages and contorting the syntax. I hope D doesn't go down that route.
Dec 16 2018
next sibling parent reply Ethan <gooberman gmail.com> writes:
On Sunday, 16 December 2018 at 22:16:28 UTC, Matthew OConnor 
wrote:
 I am a C++ developer by day and reading that article made me 
 cringe. C++ has this creeping, expansive, everything ends up in 
 the language or writable in it, if you accept awful messages 
 and contorting the syntax. I hope D doesn't go down that route.
Sweet zombie Jesus I just read the article and wow I can't believe that was written _this week_. Named arguments by defining non-thread-safe statics? People write this tripe in 2018? And get _good comments_ on it? Something has gone very wrong in compsci education.
Dec 17 2018
parent Atila Neves <atila.neves gmail.com> writes:
On Monday, 17 December 2018 at 19:24:06 UTC, Ethan wrote:
 On Sunday, 16 December 2018 at 22:16:28 UTC, Matthew OConnor 
 wrote:
 I am a C++ developer by day and reading that article made me 
 cringe. C++ has this creeping, expansive, everything ends up 
 in the language or writable in it, if you accept awful 
 messages and contorting the syntax. I hope D doesn't go down 
 that route.
Sweet zombie Jesus I just read the article and wow I can't believe that was written _this week_. Named arguments by defining non-thread-safe statics? People write this tripe in 2018? And get _good comments_ on it? Something has gone very wrong in compsci education.
At least the static variables are const...
Dec 18 2018
prev sibling parent reply JN <666total wp.pl> writes:
On Sunday, 16 December 2018 at 22:16:28 UTC, Matthew OConnor 
wrote:
 I am a C++ developer by day and reading that article made me 
 cringe. C++ has this creeping, expansive, everything ends up in 
 the language or writable in it, if you accept awful messages 
 and contorting the syntax. I hope D doesn't go down that route.
Well that's the problem. If it was implemented as a language feature it'd be a clean solution with nice syntax. But trying to force it with templates will always look ugly.
Dec 17 2018
parent reply Atila Neves <atila.neves gmail.com> writes:
On Monday, 17 December 2018 at 21:11:03 UTC, JN wrote:
 On Sunday, 16 December 2018 at 22:16:28 UTC, Matthew OConnor 
 wrote:
 I am a C++ developer by day and reading that article made me 
 cringe. C++ has this creeping, expansive, everything ends up 
 in the language or writable in it, if you accept awful 
 messages and contorting the syntax. I hope D doesn't go down 
 that route.
Well that's the problem. If it was implemented as a language feature it'd be a clean solution with nice syntax. But trying to force it with templates will always look ugly.
IMHO, there's no point, just wrap every parameter in a struct. Is there really that much of a difference between: displayCoolName(firstName = "James", lastName = "Bond") and displayCoolName(FirstName("James"), LastName("Bond")) ? It's not even fewer characters! It's not trivial but it's possible to make the order not matter by using a templated implementation and aliasing it to `displayCoolName`.
Dec 18 2018
next sibling parent JN <666total wp.pl> writes:
On Tuesday, 18 December 2018 at 10:44:28 UTC, Atila Neves wrote:
 IMHO, there's no point, just wrap every parameter in a struct. 
 Is there really that much of a difference between:

 displayCoolName(firstName = "James", lastName = "Bond")

 and

 displayCoolName(FirstName("James"), LastName("Bond"))

 ?

 It's not even fewer characters! It's not trivial but it's 
 possible to make the order not matter by using a templated 
 implementation and aliasing it to `displayCoolName`.
Well, but then you have to create structs for everything you want to pass to the method. And you have to explicitly define which have to mark arguments as named or anything like that, you just pass the names at the call site.
Dec 18 2018
prev sibling parent reply aliak <something something.com> writes:
On Tuesday, 18 December 2018 at 10:44:28 UTC, Atila Neves wrote:
 On Monday, 17 December 2018 at 21:11:03 UTC, JN wrote:
 On Sunday, 16 December 2018 at 22:16:28 UTC, Matthew OConnor 
 wrote:
 I am a C++ developer by day and reading that article made me 
 cringe. C++ has this creeping, expansive, everything ends up 
 in the language or writable in it, if you accept awful 
 messages and contorting the syntax. I hope D doesn't go down 
 that route.
Well that's the problem. If it was implemented as a language feature it'd be a clean solution with nice syntax. But trying to force it with templates will always look ugly.
IMHO, there's no point, just wrap every parameter in a struct. Is there really that much of a difference between: displayCoolName(firstName = "James", lastName = "Bond") and displayCoolName(FirstName("James"), LastName("Bond")) ? It's not even fewer characters! It's not trivial but it's possible to make the order not matter by using a templated implementation and aliasing it to `displayCoolName`.
You also have to build it: func toHex(r: Int, g: Int, b: Int) -> Int { return r << g << b; } As opposed to: struct R { int value; } struct G { int value; } struct B { int value; } int toHex(R r, G g, B b) { return r.value << g.value << b.value; } That's 67 vs 146 characters. Not to mention one pollutes the namespace and one doesn't.
Dec 18 2018
parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 18 December 2018 at 11:08:43 UTC, aliak wrote:
 It's not even fewer characters! It's not trivial but it's 
 possible to make the order not matter by using a templated 
 implementation and aliasing it to `displayCoolName`.
You also have to build it: func toHex(r: Int, g: Int, b: Int) -> Int { return r << g << b; } As opposed to: struct R { int value; } struct G { int value; } struct B { int value; } int toHex(R r, G g, B b) { return r.value << g.value << b.value; } That's 67 vs 146 characters. Not to mention one pollutes the namespace and one doesn't.
No need to pollute the namespace: struct args { template opDispatch(string name) { alias opDispatch = Arg!name; } } struct Args(T) { template opDispatch(string name) { alias opDispatch = Arg!(name, T); } } struct Arg(string name) { static auto opAssign(T)(T value) { return Arg!(name, T)(value); } } struct Arg(string name, T) { T value; alias value this; } void fun(Args!int.foo foo) {} // Unfortunately requires repetition of arg name. :( unittest { fun(args.foo = 3); } -- Simen
Dec 18 2018
parent reply aliak <something something.com> writes:
On Tuesday, 18 December 2018 at 11:15:26 UTC, Simen Kjærås wrote:
 On Tuesday, 18 December 2018 at 11:08:43 UTC, aliak wrote:
 It's not even fewer characters! It's not trivial but it's 
 possible to make the order not matter by using a templated 
 implementation and aliasing it to `displayCoolName`.
You also have to build it: func toHex(r: Int, g: Int, b: Int) -> Int { return r << g << b; } As opposed to: struct R { int value; } struct G { int value; } struct B { int value; } int toHex(R r, G g, B b) { return r.value << g.value << b.value; } That's 67 vs 146 characters. Not to mention one pollutes the namespace and one doesn't.
No need to pollute the namespace: struct args { template opDispatch(string name) { alias opDispatch = Arg!name; } } struct Args(T) { template opDispatch(string name) { alias opDispatch = Arg!(name, T); } } struct Arg(string name) { static auto opAssign(T)(T value) { return Arg!(name, T)(value); } } struct Arg(string name, T) { T value; alias value this; } void fun(Args!int.foo foo) {} // Unfortunately requires repetition of arg name. :( unittest { fun(args.foo = 3); } -- Simen
Woah... that's quite neat. I feel like this is how std.typecons.Flag should have been made!! I played around with it for a bit, and if there was a way to get some better error messages then that could be quite nice to use! - i dunno, maybe through some static assert on the "string names"s being passed in or something... Cheers, - Ali
Dec 18 2018
parent Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 18 December 2018 at 18:23:20 UTC, aliak wrote:
 Woah... that's quite neat.

 I feel like this is how std.typecons.Flag should have been 
 made!!

 I played around with it for a bit, and if there was a way to 
 get some better error messages then that could be quite nice to 
 use! - i dunno, maybe through some static assert on the "string 
 names"s being passed in or something...
Thanks, but it has some serious drawbacks - like this: void fun(Args!byte.foo foo) {} unittest { fun(args.foo = 3); // function foo.fun(Arg!("foo", byte) foo) is not callable using argument types (Arg!("foo", int)) } For a limited use case like std.typecons.Flag, it could work pretty well, though: struct Flag { alias opDispatch(string name) = FlagImpl!name; } template FlagImpl(string name) { // mixin to embetter type name. mixin(`struct `~name~` { bool value; alias value this; static typeof(this) opAssign(bool value) { return typeof(this)(value); } }`); mixin("alias FlagImpl = "~name~";"); } void fun(Flag.exclusive exclusive) { assert(exclusive); } unittest { fun(Flag.exclusive = true); } -- Simen
Dec 19 2018
prev sibling parent reply Kagamin <spam here.lot> writes:
On Friday, 14 December 2018 at 20:13:42 UTC, bauss wrote:
 In C++ you can achieve named arguments using templates and 
 operator overload:

 It's sad that D is still against having named arguments, but 
 it's possible in something that D thrives to be better than.

 https://www.fluentcpp.com/2018/12/14/named-arguments-cpp/
https://blog.jetbrains.com/dotnet/2018/11/27/inline-parameter-name-hints-c-vb- et-resharper-rider/ resharper 2018 just overlays parameter names right in editor window.
Dec 19 2018
parent reply JN <666total wp.pl> writes:
On Wednesday, 19 December 2018 at 13:30:57 UTC, Kagamin wrote:
 https://blog.jetbrains.com/dotnet/2018/11/27/inline-parameter-name-hints-c-vb-
et-resharper-rider/ resharper 2018 just overlays parameter names right in
editor window.
I think there are two usecases that are kind of getting mixed here, correct me if I'm wrong. Named/keyword arguments provide two benefits: 1) you know what the arguments mean at the call place 2) you can make optional arguments without making multiple function overloads or using something like the Builder pattern This fixes 1) but doesn't fix 2)
Dec 19 2018
parent Neia Neutuladh <neia ikeran.org> writes:
On Wed, 19 Dec 2018 15:07:48 +0000, JN wrote:
 On Wednesday, 19 December 2018 at 13:30:57 UTC, Kagamin wrote:
 https://blog.jetbrains.com/dotnet/2018/11/27/inline-parameter-name-
hints-c-vb-net-resharper-rider/
 resharper 2018 just overlays parameter names right in editor window.
I think there are two usecases that are kind of getting mixed here, correct me if I'm wrong. Named/keyword arguments provide two benefits: 1) you know what the arguments mean at the call place 2) you can make optional arguments without making multiple function overloads or using something like the Builder pattern This fixes 1) but doesn't fix 2)
D has optional arguments already, but the thing that reorderable named arguments can easily get you is not having to specify optional arguments 0 through n-1 just to specify optional argument n.
Dec 19 2018