On Tue, May 5, 2020 at 7:19 PM Marco Pivetta <ocram...@gmail.com> wrote:

> Hey Theodore,
>
>
>
> On Tue, May 5, 2020 at 6:59 PM Theodore Brown <theodor...@outlook.com>
> wrote:
>
>> On Tue, May 5, 2020 at 9:11 AM Marco Pivetta <ocram...@gmail.com> wrote:
>>
>> > As mentioned some days ago, I see named parameters as an added
>> liability,
>> > rather than values.
>> > The rationale of my negativity around the topic being that a diff like
>> > following is now to be considered a BC break:
>> >
>> > ```diff
>> > -function foo($parameterName) { /* ... */ }
>> > +function foo($newParameterName) { /* ... */ }
>> > ```
>>
>> If the function is part of your own codebase,
>
>
> BC Breaks that are intra-codebase are rarely every a problem nor a point
> of discussion.
>
>
>
>
>> an IDE can automatically
>> update function calls when renaming a parameter. For functions that are
>> part of a public API, yes, parameters shouldn't be renamed outside of
>> major versions. But in my experience, it's rare for function parameters
>> that are part of public APIs to be renamed. Do you have many real-world
>> examples of where this had to happen?
>>
>
> Parameter name changes are very much normal, since (as I already posted in
> /r/php), naming is hard, and getting it right is not a one-shot effort.
>
> Also, I haven't heard of this being a big problem in other languages
>> with named arguments like Python.
>>
>
> Good question: I guess parameter renames are avoided on purpose? Not
> familiar with the python community myself, sorry.
>
> Why do you say that? For me this feature would be extremely helpful
>> when calling constructors that have several optional arguments (e.g.
>> similar to the `ParamNode` example in the RFC). Maybe you consider
>> this a bad API,
>
>
> I do in fact consider the `ParamNode` example in that API to be a bad
> candidate for named parameters, since I'd make all of the arguments in that
> signature required anyway: defaults aren't meaningful anyway, as the AST
> library where it comes from has all the context to instantiate them all
> (the caller, being the parser, will have all the parameters anyway).
>
> If you were to instantiate a `ParamNode` from, for example, a
> `ReflectionParameter`, you would probably add named ctor (or an indepentent
> factory, if in external supporting domain) to pass all parameters as well.
>
> If you were to add more optional fields, or use this in a context of an
> AST builder (BTW, nikic/php-parser has facilities for that already) a
> mutator would be efficient too:
>
> ```php
> class ParamNode extends Node {
>     public function asReference(): static
>     {
>         $instance = clone $this;
>
>         $instance->byRef = true;
>
>         return $instance;
>     }
> ```
>

Reminds me of the saying: Design patterns are just standardized workarounds
for language limitations.

Builders and withers? Those are not, intrinsically, good code. They are
workarounds for lack of good object initialization support. I should not
have to implement a large amount of builder boilerplate to make the
construction of simple objects safe and ergonomic. We're not talking about
some kind of complex multi-stage construction logic here, but the
construction of what essentially amounts to a value object.

Generally, named arguments really change what constitutes a good API and
what doesn't. Things like boolean flags to functions are considered bad
design *because* we do not have named arguments. If I pick out some random
Python API, say subprocess.run()...

   subprocess.run(args, *, stdin=None, input=None, stdout=None,
stderr=None, capture_output=False, shell=False, cwd=None, timeout=None,
check=False, encoding=None, errors=None, text=None, env=None,
universal_newlines=None)

... and show that to a PHP developer, they're probably going to tell me
that this is horrible API design. They would, of course, be wrong. It's
reasonable API design, just in a language that supports named arguments.

Anyway. Your point that named arguments expand the API surface has been
acknowledged. I don't think this issue is really avoidable, it's a rather
fundamental trade-off of named parameters. I do think you're a bit quick in
jumping to conclusions. It's not like this problem doesn't exist in
(nearly) every language supporting named arguments.

There are some things we can do to mitigate this though. One that we
recently discussed in chat is to allow methods to change parameters during
inheritance, and allow named arguments to refer to the parameter names of
the parent method as well. Renaming parameters in a way that causes
conflicts (same parameter name at different position) would cause an LSP
error. I'm not entirely convinced this is the best approach yet, but this
does address some concerns (including the "interface extraction" concern
you mention on reddit).

Another is to allow specifying the public parameter name and the private
parameter variable name separately, as is possible in Swift. This would
allow changing "parameter" names arbitrarily, without breaking the public
API. This would be a pretty direct counter to your concern, but I'm not
really sure that your concern is important enough to warrant the additional
weight in language complexity. I've never used Swift myself, so maybe this
is actually awesome and I just don't know it.

Regards,
Nikita

Reply via email to