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

Neia Neutuladh neia at ikeran.org
Fri Dec 14 23:10:35 UTC 2018


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.


More information about the Digitalmars-d mailing list