(Sorry for sending three replies to one message, but the list server said my 
reply was too long to send as just one. 
I could have made it shorter but then I would have had to omit all the example 
code.)

@rasmus:

HOWEVER, I see no reason we must pit object initializers and named parameters 
against each other as competing features. 

I think there is benefit to both, and ideally could actually be two aspects of 
the same new feature. 

Let me explain using one of your examples.

$car = new Car({ yearOfProduction => 1975, vin => "12345678"});

Conceptually let me ask, How is { yearOfProduction = 1975, vin = "12345678"} 
really any different from an instance of an anonymous class with the properties 
yearOfProduction and vin? 

From the recognition that named parameters that are collected as a group with 
braces ({}) are conceptually identical to anonymous class instances lets us to 
the insight we could use something like func_get_args(ARGS_OBJECT) to allow us 
to capture the grouped parameters as an instance of an object of anonymous 
class containing properties for each named parameter?

public function __construct(int $yearOfProduction, string $vin) {
   $args = func_get_args(ARGS_OBJECT);
   if ($args->yearOfProduction < 1900 ||  $args->yearOfProduction > date("Y")) {
      $msg = "year of production out of range:{$yearOfProduction}";
      throw new InvalidArgumentException($msg);
   }

   $this->yearOfProduction = $args->yearOfProduction;
   $this->vin = $args->vin;
}

Now assume we later realize we would be better passing in an instance instead 
of hardcoding the properties? We could change the signature, leaving all the 
rest of the code the same:

public function __construct(Customer $args) {
   if ($args->yearOfProduction < 1900 ||  $args->yearOfProduction > date("Y")) {
      $msg = "year of production out of range:{$yearOfProduction}";
      throw new InvalidArgumentException($msg);
   }

   $this->yearOfProduction = $args->yearOfProduction;
   $this->vin = $args->vin;
}

This is a critical capability in my book; the ability to refactor code to a 
more enlightened architecture without breaking it:

Of course if someone were to have used ordinal parameters when calling that 
constructor we could match their parameters to the properties of Customer in 
order of their declaration. 

So the following maps 2019 to $yearOfProduction and "abc123xyz789" to $vin:

class Car 
{
   public int $yearOfProduction;
   public string $vin;
   ...
}
$car = new Car(2019,"abc123xyz789"); 

The above could also now throw a warning so developers can find and replace all 
of those calls with calls that use the actual named parameters, but being a 
warning the code would still working until they update the calls.

> I'd prefer to see new features that work everywhere, all the time, for
> everyone - and for existing code. Rather than adding more features and
> syntax for very specific (even relatively rare) use-cases.

I completely agree with this statement, outside the context of your prior 
argument against object initializers.

Hopefully however, I have made the case that object initializers combined with 
named parameters would address a much broader range of use-cases than either of 
them would on their own?

-Mike

Reply via email to