2 of 2

> On Sep 16, 2019, at 6:22 AM, Rowan Tommins <rowan.coll...@gmail.com> wrote:
> 
> I don't particularly see them as a "group". If I write foo($a, 42), I don't
> think of $a and 42 being bound together in any way; each is passed as a
> value to one variable in the function. So that's my answer to "why not";
> they just don't feel like related ideas to me.

I think I see why we are seeing these as different.

I have been thinking of named parameters using braces to enclose them like I 
have seen some people on the list to discuss.  So looking at it like this:
foo($a, { $mol => 42 });

$a is the normal *required* parameter and `$mol` is the *optional* named 
parameter.  Using this syntax I envision the 2nd parameter could be satisfied 
with _either_ of the following function declarations, depending on use-case and 
developer preference:

public function foo(<type> $a, int $mol ) { ... }
public function foo(<type> $a, object $args={}) { ... }

I personally would (almost?) always use the latter syntax; this is the use-case 
for which object initializers would be a killer feature (for me?).  This 
approach  can almost guarantee that function signatures never need break in the 
future, and when we use a declared named class with type hints instead of 
`object` we cab guarantee that optional parameters can be type checked when 
called.

This is why simply improving the object constructor does not really address the 
use-case. Ideally a developer would be able to start out with a stdClass 
initializer unless or until the need to use a declared named class became 
obvious and clear. (And I know stdClass properties would not be type-checked. 
But later when we move to a declared named class we would not have to refactor 
calling code like we do with arrays.)

BTW, this is how I code the same use-case today, because ensuring my function 
signatures don't need to change is one of my highest programming priorities, 
even more than type checking (but I want object initializers so I can 
eventually get type checking):

public function foo(<type> $a, array $args=[]) { 
    $defaults = array(
        'mol' => null,
    );
    $args = array_merge($defaults,$args);
    ...
}
foo($a, array( 'mol' => 42 ));

> Even if they use the same syntax, those are not the same piece of
> functionality, or equivalent. The first two are certainly related, but the
> last one I just don't see the connection.

See my explanation above.

> This feels like a straw man to me: "if we don't unify named parameters with
> object initializers, everyone will use arrays". 

My evident lack of clarity had you conflating two different arguments. Here are 
the arguments delineated:

Argument #1:  If we can add one new concept with a single syntax that can 
address many different use-cases it will probably be better than adding 
multiple new concepts each with their own new syntax.

Argument #2:  If we don't offer a simplified object initializer many of the 
less-zealous programmers will just use arrays.

IMO perfection-minded programmers are the majority; they are those who just 
want to get their job done and not have to think about it too much because they 
would rather think about family, friends, sports, and extra-curricular 
activities. So these people take the easy road, not necessarily the best road. 
I work with several of them at a client right now. 

Few developers are as zealous as people on this list. Remember, this list is 
self-selecting, so it attracts those who are most passionate about developing 
in PHP to exacting levels of perfection.

> I would definitely be happy
> seeing objects being passed around more; but this syntax isn't the only way
> to encourage that, and I think its costs outweigh its gains.

What are the costs?  I don't see any costs?

But I do see costs to segregate them, i.e. two different syntaxes/concepts for 
people to learn rather than one.

BTW, I am actually not super passionate about this one issues, but I do want to 
make sure we are comparing apples to apples.

> Consider this code:
> 
> class Customer {
>    public string $name;
>    public ?int $age=null;
> }
> function doSomething(?Customer $customer=null, ?string $name=null) {
>   // ...
> }
> doSomething({name => 'John Smith'});
> Is this equivalent to:
> doSomething(new Customer{name='John Smith'}, null);
> or:
> doSomething(null, 'John Smith');

Ok, I see why you are confused. 
I was making the assumption — which I should have stated explicitly — that the 
object that can collect named parameters would always have to be the last 
parameter — no different than variadics — which means the answer to your 
question would unambiguously be a parameter error.  (It also means either named 
parameters or variadics but not both.)
If instead you had this:
function doSomething(?string $name=null, ?Customer $customer=null) {
  // ...
}

Then this:
doSomething({name => 'John Smith'});

Would unambiguously be: 
doSomething(null, new Customer{name='John Smith'});

And this:
doSomething('John Smith');

Would unambiguously be: 
doSomething('John Smith',null);

> If I change the public properties of the Customer class, does that change
> the result? 

Generally no, but yes, in one case it would throw an error.
In what I am proposing/envisioning properties in the case would only apply to 
the last passed object.  So this would error if you remove the `name` property:
doSomething({name => 'John Smith'});

But then if I was calling a function using a hypothetical named parameter and 
we renamed that parameter in the function declaration we would get an error 
too. 

> If the parameter was mandatory, would that be different? 

No. 

> If it wasn't type-hinted, would I get an anonymous object? 

Yes, definitely.

> It's all too ambiguous and context-dependent.

No, not according to the requirements I outlined above.
However, one thing we could do to make it even clearer is to require all named 
parameters to also be grouped in the declaration using braces, e.g.:
function doSomething(?string $name=null, { ?string $email, ?string $phone, 
?string address } ) {
  // ...
}
function doSomething(?string $name=null, { ?Customer $customer=null } ) {
  // ...
}

> Compare that to separate syntax for the separate features:
> 
> doSomething(new class extends Customer { string $name='John Smith'; });
> doSomething(new Customer{ name='John Smith' });
> doSomething(name => 'John Smith');
> 
> All are nice and short, but there's no ambiguity; I don't need to read the
> rest of the code to understand what each line is doing.

Given the requirement to have the object as the last parameter, all your 
examples except the last would work, assuming you used these declarations (note 
I am assuming the braces are required in the declaration for clarity):
function doSomething({ ?Customer $customer=null }) {
  // ...
}

However, if you changed the last call to the following then they would all 
work, having the same effect as your first two examples:
doSomething( { name => 'John Smith' } );

> There's definitely some interesting ideas here, but they're not all part
> of one feature,

How the features parse out — as one or as many — are all the same to me, 
assuming the resultant capabilities can still address all the same use-cases.

-Mike




Reply via email to