Jump to page: 1 2 3
Thread overview
Named arguments
Oct 24, 2017
Andrey
Oct 24, 2017
Jonathan M Davis
Oct 24, 2017
jmh530
Oct 24, 2017
H. S. Teoh
Oct 24, 2017
jmh530
Oct 25, 2017
Andrey
Oct 25, 2017
Andrea Fontana
Oct 25, 2017
Cym13
Oct 26, 2017
Jacob Carlborg
Oct 24, 2017
Jonathan M Davis
Oct 25, 2017
bauss
Oct 25, 2017
Jonathan M Davis
Oct 25, 2017
bauss
Oct 25, 2017
Jonathan M Davis
Oct 25, 2017
Gary Willoughby
Oct 25, 2017
Bastiaan Veelo
Oct 25, 2017
jmh530
Oct 25, 2017
bauss
Oct 25, 2017
jmh530
Oct 25, 2017
bauss
Oct 25, 2017
Bastiaan Veelo
Oct 27, 2017
Jacob Carlborg
Oct 27, 2017
jmh530
Oct 26, 2017
codephantom
October 24, 2017
Hello, why there are no named arguments for functions like, for example, in kotlin i.e.:

> int sum(in int a, in int b) {
>     return a + b;
> }
>
> sum(a = 1, b = 2);
October 24, 2017
On Tuesday, October 24, 2017 17:30:27 Andrey via Digitalmars-d wrote:
> Hello, why there are no named arguments for functions like, for
>
> example, in kotlin i.e.:
> > int sum(in int a, in int b) {
> >
> >     return a + b;
> >
> > }
> >
> > sum(a = 1, b = 2);

Named arguments are not something that C-based languages typically have, and D has a very strong C/C++ heritage. There are some folks who would like to see them added to the language, but a DIP would have to be created and approved by Walter and Andrei for that to happen. IIRC, in the past, Walter didn't think that they interacted well with how D does function overloading, but I don't know what he thinks now. Some folks have managed to implement them using a library solution, and if you go digging in the newsgroup/forum/mailing list history for "named parameters" or "named arguments," you should be able to find discussions on that.

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. 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. 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.

- Jonathan M Davis

October 24, 2017
On Tuesday, 24 October 2017 at 19:22:41 UTC, Jonathan M Davis 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. 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. 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.
>
> - Jonathan M Davis

Some good points. I realized that most of the languages that have named parameters are dynamically typed ones (Python/R) or ones with VMs (C#/Scala). Fortran has named parameters and function overloading, but I think it's a little quirky and not like C++ and D as far as I can tell.
October 24, 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.
October 24, 2017
On Tuesday, October 24, 2017 13:36:00 H. S. Teoh via Digitalmars-d wrote:
> On Tue, Oct 24, 2017 at 01:22:41PM -0600, Jonathan M Davis via
> > 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.

Except that things get a bit interesting with regards to stuff like .di files. Without the parameter names being mangled in, it would be possible for the parameter names in the .di files to not match the ones in the .d files. It's possible for that now, but right now, it doesn't matter, whereas with named arguments, it would matter. To some extent, you could probably get away with it, because the code that compiles against the .di files wouldn't normally be compiled against the .d files or vice versa, but it means that switching whether you used the .di files or the .d files could break code, whereas now, it wouldn't.

Now, .di files are kind of terrible to use anyway, but introducing named arguments would just make them even more fragile if the parameter names aren't part of the function's mangled name.

- Jonathan M Davis

October 24, 2017
On Tuesday, 24 October 2017 at 20:36:00 UTC, H. S. Teoh wrote:
> [snip]
> it would be more self-documenting if written as:
>
> 	// (Hypothetical syntax)
> 	auto x = foo(width: 1, height: 2, userId: 3);
>

What about something that's a little uglier, but could be done with D magic?

For instance:
auto x = foo!(["width", "height", "userId"])(1, 2, 3)

So for instance, you could have some original function, like below

@namedParam("width", "height", "userId") auto x = _foo(int width, int height, UserId userId) { }

and then some D magic inserts a new function that is like

auto foo(string[] names)(...) { }

and then processes names, mixing in the correct named/typed variables and then returning a call to _foo with those variables.
October 25, 2017
On Tuesday, 24 October 2017 at 22:08:57 UTC, Jonathan M Davis wrote:
> On Tuesday, October 24, 2017 13:36:00 H. S. Teoh via Digitalmars-d wrote:
>> On Tue, Oct 24, 2017 at 01:22:41PM -0600, Jonathan M Davis via
>> > 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.
>
> Except that things get a bit interesting with regards to stuff like .di files. Without the parameter names being mangled in, it would be possible for the parameter names in the .di files to not match the ones in the .d files. It's possible for that now, but right now, it doesn't matter, whereas with named arguments, it would matter. To some extent, you could probably get away with it, because the code that compiles against the .di files wouldn't normally be compiled against the .d files or vice versa, but it means that switching whether you used the .di files or the .d files could break code, whereas now, it wouldn't.
>
> Now, .di files are kind of terrible to use anyway, but introducing named arguments would just make them even more fragile if the parameter names aren't part of the function's mangled name.
>
> - Jonathan M Davis

Pardon me, but I don't understand how named arguments can break anything by being added, as long as you don't add an additional syntax to the function declaration.

void foo(int bar, int baz)
{
}

in .di

void foo(int bar, int baz);

...

Can be called like:

foo(1, 2);

foo(bar: 1, baz: 2); becomes foo(1, 2);

foo(baz: 2, bar: 1); becomes foo(1, 2); because it matches the argument index.

int bar = 1;
int baz = 2;
foo(bar, baz);

October 25, 2017
On Wednesday, October 25, 2017 06:23:52 bauss via Digitalmars-d wrote:
> On Tuesday, 24 October 2017 at 22:08:57 UTC, Jonathan M Davis
>
> wrote:
> > On Tuesday, October 24, 2017 13:36:00 H. S. Teoh via
> >
> > Digitalmars-d wrote:
> >> On Tue, Oct 24, 2017 at 01:22:41PM -0600, Jonathan M Davis via
> >>
> >> > 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.
> >
> > Except that things get a bit interesting with regards to stuff like .di files. Without the parameter names being mangled in, it would be possible for the parameter names in the .di files to not match the ones in the .d files. It's possible for that now, but right now, it doesn't matter, whereas with named arguments, it would matter. To some extent, you could probably get away with it, because the code that compiles against the .di files wouldn't normally be compiled against the .d files or vice versa, but it means that switching whether you used the .di files or the .d files could break code, whereas now, it wouldn't.
> >
> > Now, .di files are kind of terrible to use anyway, but introducing named arguments would just make them even more fragile if the parameter names aren't part of the function's mangled name.
> >
> > - Jonathan M Davis
>
> Pardon me, but I don't understand how named arguments can break anything by being added, as long as you don't add an additional syntax to the function declaration.
>
> void foo(int bar, int baz)
> {
> }
>
> in .di
>
> void foo(int bar, int baz);
>
> ...
>
> Can be called like:
>
> foo(1, 2);
>
> foo(bar: 1, baz: 2); becomes foo(1, 2);
>
> foo(baz: 2, bar: 1); becomes foo(1, 2); because it matches the
> argument index.
>
> int bar = 1;
> int baz = 2;
> foo(bar, baz);

The issue I'm talking about is that if we had named arguments, and the names of the parameters in .di and .d files didn't match, and named arguments were used, then changing whether the .di file or .d file were used would break code. e.g.

.di:

void foo(int a, int b);

.d:

void foo(int c, int d);

Then this code

foo(a : 4, b : 7);

would compile with the .di file but not the .d file. That's not the end of the world, but it makes .di files even more fragile than they already are.

Adding named parameters would not break any existing code, but it would introduce more ways to break code. Right now, no code anywhere has to care about parameter names except the function that has those parameters. There is no dependency on those names. You don't even have to have parameter names in a .di file if you don't want to - all that matters is the types. A function's parameter names can be changed at any time without breaking _any_ code that calls that function. But as soon as we have named arguments, suddenly, you can't change parameter names anymore than you can change the name of a function, or you risk breaking code, and that's a _huge_ negative IMHO. It also means more bikeshedding over names, because suddenly the parameter names are part of the API. It's even worse when you consider that most parameter names in existing code were not chosen with the idea that they would be part of the function's API, because we don't currently have named arguments in D.

IMHO, it's already annoying enough how locked down things are once you put them out in the wild for others to use (at least if you care about not breaking the code of anyone using any code you make available). I don't want to see yet more added to the list of things that you can't change, because it might break someone's code, and adding named arguments to D definitely would do that, because it would make the parameter names part of the API, which they're not right now.

- Jonathan M Davis

October 25, 2017
On Tuesday, 24 October 2017 at 20:36:00 UTC, H. S. Teoh wrote:
> 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.
> ...

good alternative, I already forgot about the power of structs after Java.
October 25, 2017
On Tuesday, 24 October 2017 at 17:30:27 UTC, Andrey wrote:
> Hello, why there are no named arguments for functions like, for example, in kotlin i.e.:
>
>> int sum(in int a, in int b) {
>>     return a + b;
>> }
>>
>> sum(a = 1, b = 2);

This has been discussed to death:

http://forum.dlang.org/post/n8024o$dlj$1@digitalmars.com

and you can do it as a library so no need to go in the language:

https://github.com/CyberShadow/ae/blob/master/utils/meta/args.d
« First   ‹ Prev
1 2 3