Named arguments

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Oct 24 20:36:00 UTC 2017


On Tue, Oct 24, 2017 at 01:22:41PM -0600, Jonathan M Davis via Digitalmars-d wrote:
[...]
> Personally, I don't want them in D. If you have enough arguments that
> it matters, then the function probably has too many parameters or too
> many similar parameters.

If a function has too many parameters, or only a small subset of
parameters need to be something other than some default value, or the
set of parameters may change frequently, then my default approach is to
abstract the parameters into a struct:

	struct OutputArgs {
		string filename;
		int width = 300;
		int height = 300;
		string fontDir = "/usr/share/local/fonts/arial.ttf";
		int fontSize = 12;
		Color background = Color.black;
		Color foreground = Color.white;
		bool antiAlias = true;
	}

	void generateOutput(OutputArgs args) { ... }

	void main() {
		// Setup function arguments.
		// N.B.: only .filename needs to be explicitly set.
		OutputArgs args;
		args.filename = "/tmp/output.png";

		// Call function with mostly default arguments.
		generateOutput(args);
	}

This approach means that if you ever need to add more parameters to
OutputArgs, as long as the default value is compatible with previous
behaviour, you won't have to change existing code. Also, the caller can
set the arguments in any order without needing to memorize which
parameter is in which position.


> And as a library writer, I don't want to have the parameter names be
> part of the API. There are already enough problems getting the type
> and function names right without having to worry about bikeshedding
> over parameter names as well, and if we had named arguments, then you
> couldn't change parameter names without breaking code.

There are times, however, when named parameters might be desirable.
Self-documenting code is one. Rather than:

	auto x = foo(1, 2, 3);

it would be more self-documenting if written as:

	// (Hypothetical syntax)
	auto x = foo(width: 1, height: 2, userId: 3);

There are ways around this, of course.  Here's a somewhat klunky, but
workable solution:

	struct Width {
		int value;
		alias value this;
	}
	struct Height {
		int value;
		alias value this;
	}
	struct UserId {
		int value;
		alias value this;
	}
	int foo(Width w, Height h, UserId id) { ... }

	auto x = foo(Width(1), Height(2), UserId(3));

Inside the body of foo, you can freely assign w, h, and id to int
variables, because of the `alias this`.  But the declared parameter
types of foo demands the user to use self-documenting syntax.

And presumably, if your code already has values of the requisite types,
you can pass them directly without needing to repeat the constructor
name:

	int bar(Width w, Height h) {
		UserId id = getUserId();
		foo(w, h, id);	// no need to type Width(w), etc.
	}

But of course, this scheme only works well if you have a common set of
parameters that are used in many places.  Otherwise you'll end up having
to declare tons of structs that are only ever used to call 1 or 2
functions, which would be a lot of typing for little benefit.  You
could, of course, write a mixin template to factor out the boilerplate
(somewhat), but even that doesn't necessarily cover all possible use
cases.


> It also wouldn't play well with separate compilation unless the
> parameter names were mangled into the function names, and symbol names
> in D are already too often too long due to idioms like Voldemort types
> creating really long symbol names. Recent work on the compiler has
> reduced that problem, but adding more information to mangled names
> would definitely not help.
[...]

Nah.  Parameter names are a concept only necessary at compile-time.  All
you need is to import the function declaration with the right parameter
names, and on the caller's side the compiler will always emit the
arguments in the right order.  So no need to mangle parameter names at
all.


T

-- 
An imaginary friend squared is a real enemy.


More information about the Digitalmars-d mailing list