June 07, 2019
On 07.06.19 04:51, Timon Gehr wrote:
> argument names

parameter names
June 07, 2019
On 07.06.19 04:51, Timon Gehr wrote:
> 
> import std.traits;

Unnecessary import. I should go to sleep.
June 07, 2019
On Friday, 7 June 2019 at 02:51:16 UTC, Timon Gehr wrote:
> On 07.06.19 01:36, Jonathan Marler wrote:
>> On Thursday, 6 June 2019 at 23:08:56 UTC, Exil wrote:
>>> On Thursday, 6 June 2019 at 22:35:31 UTC, Jonathan Marler wrote:
>>>> On Thursday, 6 June 2019 at 20:25:32 UTC, Exil wrote:
>>>>> [...]
>>>>
>>>> Yes I am making the same argument as for structs/classes. Structs and classes can "opt-in" and "opt-out" to exposing their fields.  Saying that all function parameter names should be exposed and coupled to the caller is like saying we shouldn't have private fields in structs and classes.
>>>
>>> That doesn't make any sense. To make a function parameter "private" that would mean the user wouldn't be able to pass in a value to it.
>> 
>> That's not the meaning I proposed.  I proposed that public exposes the "name" of the function parameter.  You're "straw manning" again,
>
> No, he's just not arguing against the aspect of your idea that you think he is. You are the one setting up the straw man.
>

He said "To make a function parameter 'private' that would mean the user wouldn't be able to pass in a value to it".  My idea had nothing to do with preventing callers from passing values to arguments, hence why he is straw manning me.

>>> The only benefit you get from having names be optional is that library maintainers can screw over their user base if they want to use the feature or not.
>> 
>> Again, the benefit is Encapsulation.
>
> Well, let's look at the facts:
>
> - Parameter names are already exposed, and changing parameter names can already break third-party code:
> https://dlang.org/phobos/std_traits.html#ParameterIdentifierTuple
>
> No "encapsulation" for you. (Whatever that's supposed to mean.)
>
> - It is possible to not name parameters (an alternative way is to use *.di files):
>
> import std.traits;
> int add(int, int){
>     return _param_0+_param_1;
> }
> void main(){
>     assert(add(1,2)==3);
> }
>
> I guess that's what you would call "encapsulation".
>
> I.e., if you want to not expose any parameter names, you have the means to do so. Let's call those two ways to declare parameters public and private. What named arguments allow you to do is to actually refer to the public parameters by name.
>
> If we had structs with public and private fields, there was no field access (e.f) syntax and you could only access public fields by index, would you also argue that this is a good thing, even if there was already a __trait to get the index of a field from its name and vice-versa?

Hmmm that's a good point, I suppose they are already exposed to the caller.  However, I believe private fields in structs/classes have the same problem.  You can still access those fields with some fancy reflection, but I don't think that means we should get rid of private fields just because we can still access them through reflection.  Do you?
June 07, 2019
On Friday, 7 June 2019 at 02:44:19 UTC, Exil wrote:
> On Thursday, 6 June 2019 at 23:36:11 UTC, Jonathan Marler wrote:
>> On Thursday, 6 June 2019 at 23:08:56 UTC, Exil wrote:
>>> On Thursday, 6 June 2019 at 22:35:31 UTC, Jonathan Marler wrote:
>>>> On Thursday, 6 June 2019 at 20:25:32 UTC, Exil wrote:
>>>>> On Thursday, 6 June 2019 at 20:10:06 UTC, Jonathan Marler wrote:
>>>>>> Far to high of a cost just to allow some cases to be more readable.  However, if you make it opt-in, then you don't have to pay that cost for EVERY function, you only increase coupling in the cases that make sense.
>>>>>
>>>>> Making it optional makes it useless. Name the programming languages that implement named parameters this way, and I'll give a giant list of ones that don't. You can make this exact argument for basically structs and everything else.
>>>>
>>>> Yes I am making the same argument as for structs/classes.  Structs and classes can "opt-in" and "opt-out" to exposing their fields.  Saying that all function parameter names should be exposed and coupled to the caller is like saying we shouldn't have private fields in structs and classes.
>>>
>>> That doesn't make any sense. To make a function parameter "private" that would mean the user wouldn't be able to pass in a value to it.
>>
>> That's not the meaning I proposed.  I proposed that public exposes the "name" of the function parameter.  You're "straw manning" again, please look that one up.  Understand what I'm saying before you respond.
>
> It's what private currently means. You are misunderstanding what I am saying and throwing around strawman like it applies. Encapsulation/private/public do not relate at all to what you are suggesting.

I suggested that we could re-use the `public` keyword to allow a function to expose their parameter names to the caller.  It works because `public` currently has no meaning when you apply it to function parameters.  But you seem to be assigning the `private` modifier a meaning to function parameters that doesn't exist and that I didn't suggest, hence you are straw-manning my idea.

And the reason why this is "encapsulation" is because you are "encapsulating" the names of parameters from the caller, meaning that the library is free to change them without breaking compatibility.  Just like a library owner knows they can change their private field names without breaking compatibility with their library.

>
>>> Cause it doesn't make sense, not one wants to deal with that address complexity because it provides zero benefit.
>>
>> Encapsulation = Zero Benefit?
>>
>> Well I can see we're not going to agree here :)
>
> This is not encapsulation. You are conflating the two. Talk about strawman...

"conflating the two"?  Conflating encapsulation and what else?

Encapsulation is the only point I'm making here.

>
>>>>> When you use a library you are at the mercy of it's owner. I've seen some people maintain an API that just literally changed the style so basically every single line that used that API now had to be updated.
>>>>
>>>> Even more reason not expose all the parameter names of all the functions in their library.  If the library owner changes any of their parameters names, they have broken compatibility with their library. This increases the opportunity for breakage dramatically.  That's the power and benefit of encapsulation, being able to change the internals without breaking your external facing API.
>>>
>>> That power is entirely depending on the person writing it. If the maintainer wants to keep backwards compatibility with named arguments it is very easy to do so. This is NOT encapsulation, you can still access parameters. Making it optional won't make maintainers be good maintainers, it will just make the users suffer and avoid the feature for its uselessness.
>>>
>>
>> You seem to be confusing what I said.  I didn't say the parameters are encapsulated...that doesn't make any sense.  I'm talking about encapsulating the 'names' of the parameters.
>>  I think once you understand the distinction you'll see you're response here doesn't make much sense.
>
> And you are going on and on about encapsulation... I understand what you are saying. That is not encapsulation, and what you are proposing is not useful. It prevents a useful feature from being used, if API maintainers don't want to maintain it, then they don't have to. Just like everything else. But then no one will use their API, and/or people will complain and it will be fixed.

There's an expression for what I just read in this last paragraph.  "Talking out of both sides of your mouth". You said "This is not encapsulation" and then immediately afterwards said that if an API maintainer doesn't maintain this feature, then it will break their API and users won't use it.

If you can't understand why you contradicted yourself here, there's really nothing I can do.

>
>>>>> There's already enough attributes, we shouldn't be adding more needlessly. I don't think this is a good enough reason to.
>>>>
>>>> I didn't say we should add an attribute.  You're now demonstrating the "straw man fallacy" (https://en.wikipedia.org/wiki/Straw_man).
>>>
>>> What the, this isn't even the main argument and your pulling that card? Come on now. I would be interested in hearing how you will mark one function as optional and another as not optional though. Someone suggested @named, I have yet to hear anything better from you.
>>
>>
>> You just need a way to distinguish named from unnamed.  Opt-in and Opt out.  You could do so by putting all your named arguments in a struct and supporting some caller syntax to expand arguments into that struct.  You could use tuples, you could use a special type wrapper. And yes, you could use an attribute like "public", but you don't have too is the point.
>
> Those all sound like terrible ideas. I need an extra struct to be able to use named and unnamed functions? So now named parameters dictate the design? I can only imagine the additional problems this would add. A special type wrapper might be worse than an extra attribute.
>
>> I'm just weighing the pros and cons here.  I see a big con to enabling this by default for every function in D.  I see a small benefit from the feature altogether, as it's only useful for a small percentage of functions.
>
> It doesn't matter what percentage. That's the thing, you might say this section of is more important and should use named parameters. But I disagree, and I think this other section should have named parameters. But you are the library maintainer, so you make your choice and you chose to ignore mine. Now I use your library and I am restricted to which functions I can use a language feature on. I can't use the language feature where I want to because of the maintainer's opinion.
>
> struct A {
> private:
>     int a;
> }
>
>  A a;
>  a.a = 10; // error can't do it
>
> void foo( int value );
>
> foo( value: 10 ); // error not defined to use named parameters
> foo( 10 ); // functionally the same
>
> The whole idea of "encapsulating" the variable name is completely bonkers to me. It's a feature that is mislabeling something as encapsulation and is trying to shoehorn it is with statements on the basis that "encapsulation" can't be bad. It is when you don't know what it is or why it is useful.

I'm very confused why you keep saying that hiding parameter names isn't "encapsulation". The point is that if you expose the parameters names of all functions, you've increased the coupling between all functions and their callers and therefore decreased encapsulation.

If you want to say that parameters names are not very useful to encapsulate, then that's fine, you can have that opinion.  But saying that it's not encapsulation at all is demonstrably false.

You also said the "percentage didn't matter", which may be the case for you but not for me.

For me, the percentage of functions that would benefit from this tells me whether it should be "opt-in", "opt-out" or "always on" (the current DIP has proposed "always on"). If there is no downside to enabling them, then I would be fine with "always on", even if there usefulness is low.  But because I do see a big downside, and I think only about 15% of functions can benefit from it, it seems to me an "opt-in" option makes sense here.

Note here that I could also be convinced otherwise, but saying "this is not encapsulation" is not a convincing argument.

One thing that would be beneficial to discuss is what that percentage is.  I've actually proposed named parameters be added to another language (zig) here: https://github.com/ziglang/zig/issues/479

I came up with a list of cases when named arguments are helpful:

1. flag type parameters

add(1, 2, true); // what is true for?
add(1, 2, checkOverflow=true); // there we go

However in D, this one can be mitigated with Flag.  It's a bit less convenient though.

2. if the parameter is a "unit type" like seconds/meters

sleep(1); // is this seconds or milliseconds or what?
sleep(seconds = 1); // much better

D also has ways to mitigate this one as well, such as abstract types like Duration.  But like flag, these can be less convenient.

3. if the function takes multiple parameters of the same type whose order is important but not clear

can't think of a good example for this one at the moment


By my estimate the percentage of functions that fit any of these 3 criteria is around 15%. This isn't backed by any real data, just an estimate.  I would love to see actual data on this, if someone wants to go through some druntime/phobos modules and count how many functions would benefit from named arguments.  And my criteria list could also be incomplete, I'm sure there are other cases where named parameters could be useful.  Please let me know if you can think of any more.  And if it turns out the percentage is much higher than my estimate, then I might change my mind and say that decreasing encapsulation is worth the benefit of having named parameters "always on".

June 07, 2019
On Friday, 7 June 2019 at 03:52:24 UTC, Jonathan Marler wrote:
> By my estimate the percentage of functions that fit any of these 3 criteria is around 15%. This isn't backed by any real data, just an estimate.  I would love to see actual data on this, if someone wants to go through some druntime/phobos modules and count how many functions would benefit from named arguments.  And my criteria list could also be incomplete, I'm sure there are other cases where named parameters could be useful.  Please let me know if you can think of any more.  And if it turns out the percentage is much higher than my estimate, then I might change my mind and say that decreasing encapsulation is worth the benefit of having named parameters "always on".

D has features that are useful for a *lot* less than 15% of cases. If you think named parameters are so harmful that 15% is too low a rate to bring them in, then you basically dislike named parameters as a feature. But this is not a disagreement on merits - the things you dislike *are* the things that I like about named parameters. I think we have to separate "I don't like these parts of this feature" and "I think these parts don't *work*".

The very notion of "Named Arguments Lite" is in my opinion the consequence of trying to appeal to people who just don't find named arguments appealing.

I think we need to separate "problems with named arguments" and "problems with this DIP for named arguments" (of which there's still plenty). If you think that named arguments, or rather opt-out named arguments, are a bad idea, there should be a separate language design thread to debate that question.
June 07, 2019
On Friday, 7 June 2019 at 05:18:51 UTC, FeepingCreature wrote:
> On Friday, 7 June 2019 at 03:52:24 UTC, Jonathan Marler wrote:
>> By my estimate the percentage of functions that fit any of these 3 criteria is around 15%. This isn't backed by any real data, just an estimate.  I would love to see actual data on this, if someone wants to go through some druntime/phobos modules and count how many functions would benefit from named arguments.  And my criteria list could also be incomplete, I'm sure there are other cases where named parameters could be useful.  Please let me know if you can think of any more.  And if it turns out the percentage is much higher than my estimate, then I might change my mind and say that decreasing encapsulation is worth the benefit of having named parameters "always on".
>
> D has features that are useful for a *lot* less than 15% of cases. If you think named parameters are so harmful that 15% is too low a rate to bring them in, then you basically dislike named parameters as a feature.

I'm not sure where you got that, I never said I didn't want named parameters.  I do want to bring named parameters in even though I only think they apply to 15% of cases.  I'm just pointing out a reason for making them "opt-in" instead of "always on" with no way to disable them.

I think there are other proposals that provide the same benefits without the drawbacks.  Walter's proposal may be one, Timon's tuple proposal should be considered as well to make sure we don't design a feature that won't work if we decide to implement tuples.

> But this is not a disagreement on merits - the things you dislike *are* the things that I like about named parameters. I think we have to separate "I don't like these parts of this feature" and "I think these parts don't *work*".

I think there are pros and cons to exposing all parameters names to all callers.  The big con is that it adds extra coupling between every function and caller that in the majority of cases is not beneficial (at least by my estimate, having data will show whether or not it is actually the majority).  But there are benefits to enabling named parameters for all functions such as allowing the feature to be used without changing libraries, and it's backwards compatible.  I can see the pros and the cons, I'm just pointing out the con, but I can see where you're coming from if you think the pros outweigh the con.  What some people on this thread are saying is that the CON doesn't exist, so I'm focusing on explaining why it does exist which probably makes it sound like I don't want the feature at all.

>
> The very notion of "Named Arguments Lite" is in my opinion the consequence of trying to appeal to people who just don't find named arguments appealing.
>
> I think we need to separate "problems with named arguments" and "problems with this DIP for named arguments" (of which there's still plenty). If you think that named arguments, or rather opt-out named arguments, are a bad idea, there should be a separate language design thread to debate that question.

Well this DIP isn't even "opt-out", at least for the function definition.  If I want to keep my parameters names private so that I can change them without breaking compatibility, I have no way to do it.  It's like not having a way to make fields private in a struct or class.  Though to be fair, exposing field names vs parameters names are not the same thing, but the drawback shouldn't be dismissed.  Instead it should be analyzed and weighed against everything else.

But you are right that this DIP should not be the "make or break" for named parameters, and I don't think anyone is saying it is.  I'm just pointing out a flaw with this particular DIP, not with named arguments in general.

June 07, 2019
On Thursday, 6 June 2019 at 20:04:15 UTC, Walter Bright wrote:
> Although this supports reordering, the real reason for naming is so one can
> have
> a function with a longish list of parameters, each with a reasonable default,
> and the user need only supply the arguments that matter for his use case. This
> is much more flexible than the current method of putting all the defaults at
> the
> end of the parameter list, and defaulting one means all the rest get defaulted.
>
> A secondary reason is (again) for a longish list of parameters, when the user
> finds himself counting parameters to ensure they line up, then named parameters
> can be most helpful.

It's also useful for

1. boolean flags

foo(true);
foo(log: true);

2. parameters with units

sleep(1);
sleep(seconds: 1);

3. parameters where the order matters but is not obvious

copy(a, b);
copy(src: a, dst: b);

Of course there are other ways to accomplish these such as using abstract types like Flag and Duration to specify names and units or establishing name conventions to indicate parameter ordering.  But named parameters also make these cases pretty convenient.

June 06, 2019
On 6/6/2019 2:43 PM, Aliak wrote:
> On Thursday, 6 June 2019 at 20:04:15 UTC, Walter Bright wrote:
>>    discovered as necessary behavior. Consider:
>>
>>     void snoopy(T t, int i, S s);     // A
>>     void snoopy(S s, int i = 0; T t); // B
>>
>> and calling it:
>>
>>     snoopy(t:t, i, s:s); // A
>>     snoopy(s:s, t:t, i); // A
> 
> Trying to understand why the last two call A and are not ambiguous?

Argument `i` is not named, and so can only appear after `t` in A, and only after `s` in B. This only works for A.

June 07, 2019
On Friday, 7 June 2019 at 06:30:15 UTC, Walter Bright wrote:
> On 6/6/2019 2:43 PM, Aliak wrote:
>> On Thursday, 6 June 2019 at 20:04:15 UTC, Walter Bright wrote:
>>>    discovered as necessary behavior. Consider:
>>>
>>>     void snoopy(T t, int i, S s);     // A
>>>     void snoopy(S s, int i = 0; T t); // B
>>>
>>> and calling it:
>>>
>>>     snoopy(t:t, i, s:s); // A
>>>     snoopy(s:s, t:t, i); // A
>> 
>> Trying to understand why the last two call A and are not ambiguous?
>
> Argument `i` is not named, and so can only appear after `t` in A, and only after `s` in B. This only works for A.

What makes i unnamed and the others named?

You mean you want for unnamed arguments to only occur after the last named argument?
June 07, 2019
On 6/7/2019 1:07 AM, aliak wrote:
> What makes i unnamed and the others named?

  snoopy(t:t, i, s:s);
              ^ note this is not i:i