February 28, 2011
On 02/28/2011 04:02 PM, Steven Schveighoffer wrote:
> On Mon, 28 Feb 2011 09:55:54 -0500, Michel Fortin <michel.fortin@michelf.com>
> wrote:
>
>> On 2011-02-28 09:34:30 -0500, spir <denis.spir@gmail.com> said:
>>
>>> So, let's use ':'.
>>
>> Note however that ':' already has a meaning in templates arguments. Using ':'
>> for named function arguments would preclude having named template arguments
>> (or force template arguments to use yet another syntax).
>
> I think you are confusing template declarations with template instantiations.
> ':' is not used in instantiations (that I know of)
>
> e.g. the usage would be:
>
> template foo(T = int, U = string) {...}
>
> foo!(U : int)...;

Yes, but the semantic conflict remains: on left hand, subtyping; on right hand plain naming.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

February 28, 2011
On 2/28/11 5:48 AM, Andrei Alexandrescu wrote:
> One more thing, order of evaluation should still be left-to-right, not
> in order of arguments. This means the feature cannot be a syntactic
> rewrite (not a big issue, but definitely something to keep in mind).
>
>
> Andrei

I was thinking that order of evaluation should remain lexically left-to-right at the point of call (that is, in the order the arguments are specified, with any remaining default parameters coming after); is there a reason that would be bad or wouldn't work?
February 28, 2011
Andrei:

> One more thing, order of evaluation should still be left-to-right, not in order of arguments. This means the feature cannot be a syntactic rewrite (not a big issue, but definitely something to keep in mind).

In this case then I suggest the compiler to just refuse the compilation of that line of code. Better to disallow something than allowing something that breaks one of the (future!) D rules.

Bye,
bearophile
February 28, 2011
On Sunday, February 27, 2011 23:03:46 Bekenn wrote:
> Consider the following line from a project I'm playing around with:
> 
> 	HRESULT hr = m_Device.Present(null, null, null, null);
> 
> Quick: What behavior does that third argument specify?  If you said, "Well, /obviously/, that's a handle to an alternative destination window for the render operation; by passing null, you're choosing not to provide an alternative render target." then... well, gee, I have no answer to that.
> 
> With named arguments, it becomes a bit easier to understand:
> 
> 	HRESULT hr = m_Device.Present(pSourceRect: null, pDestRect: null,
> hDestWindowOverride: null, pDirtyRegion: null);
> 
> If I remember right, Python has this (optional) feature; I'm not aware of anyone ever complaining about it.  It also nicely provides a solution to the bool argument problem: http://blogs.msdn.com/b/oldnewthing/archive/2006/08/28/728349.aspx
> 
> But wait, there's more!
> 
> Named arguments get a +1 synergy bonus with default arguments.  When using parameter names to specify arguments, the order in which those arguments are passed no longer matters.  Imagine if the Present() method above provided the default argument null for each parameter:
> 
> interface IDirect3DDevice9 : IUnknown
> {
> 	...
> 	HRESULT Present(const(RECT)* pSourceRect = null, const(RECT)* pDestRect
> = null, HWND hDestWindowOverride = null, const(RGNDATA)* pDirtyRegion =
> null);
> 	...
> }
> 
> We can do this in the D binding without running afoul of any linkage issues, and it simplifies the Present() call for its most common usage, which I think is a good thing.  Now, let's say I'm doing something special; suppose I'm not worried about source and destination rectangles or dirty regions, but I do want to supply a different render target. With named arguments, that's easy to do without making things messy:
> 
> 	HRESULT hr = m_Device.Present(hDestWindowOverride: hOverride);
> 
> Named arguments should also play nicely with positional arguments:
> 
> 	auto wrappedString = wrap(reallyLongString, colmuns: 120, tabsize: 4);
> 
> Lastly, wouldn't be an entirely new feature; D already supports named arguments for static struct initializers.  I merely propose sharing the love with normal functions and struct literals.
> 
> Here are the rules I have in mind:
> 
> 1) Named arguments are not a replacement for positional arguments.
> Arguments fall into three categories: positional, named positional, and
> named non-positional.  An argument is positional if no name is supplied.
>   An argument is named positional if a name is supplied and its position
> matches the parameter's position in the function declaration.  An
> argument is named non-positional if a name is supplied and either a) its
> position does not match the parameter's position in the function
> declaration, or b) it is immediately preceded by a named non-positional
> argument.
> 
> 2) No positional argument may appear after a named non-positional argument.
> 
> 3) Named non-positional arguments may appear in any order.
> 
> 
> Potential problems:  The only problems I can foresee here are variations on the situation when there are two (or more) versions of a function with the same number, type, and names of parameters, but in non-matching order, like this:
> 
> void func(int a, char b);
> void func(char b, int a);
> 
> In such a case, the compiler should diagnose an error if named arguments are employed.
> 
> Thoughts?

I'm not entirely against named arguments being in D, however I do think that any functions that actually need them should be refactored anyway. So, ultimately, I'm not sure that they're really all that useful. I'm sure that they'd be useful upon occasion, but if you actually need them, then your function is taking too many arguments.

In actuality, if I were to vote on whether named arguments should be in the language, I would definitely vote against it (I just plain don't want the code clutter, and they strike me as a crutch to avoid writing functions with good signatures in spite of their usefulness in some situations), but I can see why some people might want them.

- Jonathan M Davis
February 28, 2011
spir:

> Yes, I'm aware of that. This usage in fact conflicts with the general "subtyping" semantics of ':'. But this conflict already exists with the usage of ':' in dyn array notation, which is linguistically pretty close to a named argument set; so...

If I have understood what you mean here, then I think we'll just have to leave with this small clash. An alternative solution is to use the Ada syntax: foo(x => 1, y=>2) but it's a bit longer because it requires two chars instead of one.

Bye,
bearophile
February 28, 2011
On 2/28/11 5:11 AM, bearophile wrote:
> In recent C# versions:
> http://msdn.microsoft.com/en-us/library/dd264739.aspx
> http://geekswithblogs.net/michelotti/archive/2009/01/22/c-4.0-named-parameters-for-better-code-quality.aspx  )

I had no idea C# had adopted this feature.  Thanks!

Looking through that first link, it looks like their rules are very nearly a match for what I came up with; the only difference I can see is that they'd prohibit the use of positional arguments following a named argument (even if the named argument is in the expected position).  My argument in favor of named positional arguments (after which you *can* continue to specify positional arguments) is that the name becomes a compiler-verified annotation.
February 28, 2011
On 2/28/11 5:59 AM, spir wrote:
> +++ Make things simple!
> Since positional arguments is the main & historic parameter-passing
> method, just keep order.

I think that would remove a huge chunk of the utility of having named arguments, and it doesn't make things easier at all from the compiler's perspective.

Consider:

Declaration:
	void func(int a = 0, int b = 1);

Call:
	func(b: 3);	// a is default

Since b in the call is not in the same position as specified in the declaration (position 0 instead of position 1), the compiler already has to ignore the positioning of named arguments.
February 28, 2011
On Mon, 28 Feb 2011 13:51:56 -0500, Jonathan M Davis <jmdavisProg@gmx.com> wrote:

> I'm not entirely against named arguments being in D, however I do think that any
> functions that actually need them should be refactored anyway. So, ultimately,
> I'm not sure that they're really all that useful. I'm sure that they'd be useful
> upon occasion, but if you actually need them, then your function is taking too
> many arguments.
>
> In actuality, if I were to vote on whether named arguments should be in the
> language, I would definitely vote against it (I just plain don't want the code
> clutter, and they strike me as a crutch to avoid writing functions with good
> signatures in spite of their usefulness in some situations), but I can see why
> some people might want them.

Although I am not strongly for named arguments, I think they would be a definite improvement.

Bearophile brought up one of the strongest cases for them:

foo(int width, int height) {}

Seems simple enough, I don't see how you have "too many arguments", but the call looks like this:

foo(123, 456);

So, looking at this call, can you tell which is width and which is height?  I've seen some libs that use width and height do height first also.  I usually have to go look up the API every time I'm reading/writing one of these.

But this is perfectly clear and resists API changes/differences:

foo(width: 123, height: 456);

The cool part about this is, named arguments are not required -- you can always just not use them.  But when you do use them, the code becomes much clearer.

-Steve
February 28, 2011
On 2/28/11 4:41 AM, Michel Fortin wrote:
> Another problem is what happens if you override a function by using
> different parameter names. For instance:
>
> class A {
> void func(int foo);
> }
> class B : A {
> void func(int bar);
> }
>
> Currently, the above is allowed. Would it now become an error?

Definitely not an error!  This feature should not break any existing code.

Method lookup already happens based on the static type of your object reference; that shouldn't change:

A a = new A;
a.func(foo = 3);	// ok
a.func(bar = 3);	// Error, argument name does not match parameter name

B b = new B;
b.func(foo = 3);	// Error, argument name does not match parameter name
b.func(bar = 3);	// ok

A c = new B;
c.func(foo = 3);	// ok
c.func(bar = 3);	// Error
February 28, 2011
Jonathan M Davis:

> I'm not entirely against named arguments being in D, however I do think that any functions that actually need them should be refactored anyway.

After your have refactored a function, I sometimes want to named arguments again. And sometimes you can't refactor an API, etc.


> (I just plain don't want the code clutter,

It's about as clutter as giving names to variables. And it's optional, you are not forced to call a function with named arguments in D (this sometimes happens in Python when the function accepts a **kwrg).


> and they strike me as a crutch to avoid writing functions with good signatures in spite of their usefulness in some situations),

I'd like good signatures too.

Bye,
bearophile