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