Re: [PHP-DEV] [RFC] Object Initializer

This is only part of a thread. view whole thread
  106969
September 12, 2019 15:01 kjarli@gmail.com (Lynn)
On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski <
michal.brzuchalski@gmail.com> wrote:

> Hi internals, > > I'd like to open discussion about RFC: Object Initializer. > > This proposal reduces boilerplate of object instantiation and properties > initialization in case of classes without required constructor arguments as > a single expression with initializer block. > > https://wiki.php.net/rfc/object-initializer > > I appreciate any feedback you all can provide. > > Thanks, > -- > Michał Brzuchalski > brzuchal@php.net > > Heya,
What's the added benefit of this compared to implementing a constructor? The part I like is that this can be used to replace stdClass/structured arrays. Perhaps something like this would nice to have in PHP: ``` $people = []; foreach ($peopleFromDatabase as [$id, $username, $name]) { $people[] = { Uuid id => $id, string username => $username, string name => $name, }; // and possible automatic assignment: $people[] = {Uuid $id, string $username, string $name}; } ``` Regards, Lynn van der Berg
  106971
September 12, 2019 15:07 oludonsexy@gmail.com (Olumide Samson)
The RFC is a beautiful feature suggestion, but something is telling me as
beautiful and straightforward the syntax is, what can the use case be?

I really love Constructor or Object initialization to be implemented in
PHP, but is there something I think you're missing in the RFC?

Thanks for the RFC.

On Thu, Sep 12, 2019, 4:02 PM Lynn <kjarli@gmail.com> wrote:

> On Thu, Sep 12, 2019 at 4:00 PM Michał Brzuchalski < > michal.brzuchalski@gmail.com> wrote: > > > Hi internals, > > > > I'd like to open discussion about RFC: Object Initializer. > > > > This proposal reduces boilerplate of object instantiation and properties > > initialization in case of classes without required constructor arguments > as > > a single expression with initializer block. > > > > https://wiki.php.net/rfc/object-initializer > > > > I appreciate any feedback you all can provide. > > > > Thanks, > > -- > > Michał Brzuchalski > > brzuchal@php.net > > > > > Heya, > > What's the added benefit of this compared to implementing a constructor? > > The part I like is that this can be used to replace stdClass/structured > arrays. Perhaps something like this would nice to have in PHP: > > ``` > $people = []; > > foreach ($peopleFromDatabase as [$id, $username, $name]) { > $people[] = { > Uuid id => $id, > string username => $username, > string name => $name, > }; > // and possible automatic assignment: > $people[] = {Uuid $id, string $username, string $name}; > } > ``` > > Regards, > Lynn van der Berg >
  107042
September 13, 2019 06:18 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Olumide,

czw., 12 wrz 2019 o 17:07 Olumide Samson <oludonsexy@gmail.com> napisał(a):

> The RFC is a beautiful feature suggestion, but something is telling me as > beautiful and straightforward the syntax is, what can the use case be? > > I really love Constructor or Object initialization to be implemented in > PHP, but is there something I think you're missing in the RFC? > > Thanks for the RFC. >
I'm open to suggestions. PHP is known for borrowing features from other languages and this feature is also known from other languages. * Rust allows whole struct initialization in one expression, although these are not classes but being able to instantiate, initialize and return instance directly after it's being initialized reduces a lot of boilerplate. * C# uses an object initializer for the same purposes, one expression which allows to instantiate, initialize and use the object directly after initialization. * Java users use hacks to do the same, even more, cause they're allowed to call logic through method calls inside "initializer" block. In some cases when you deal a lot with DTO objects, object instantiation and properties initialization is very verbose and introduces a lot of noise.. class CustomerDTO { public Uuid $id; public string $name; public Email $email; public Address $address; public City $city; public Country $country; public PhoneNumber $phoneNumber; public etc... } Given above DTO class instantiation and initialization now requires: * INSTANTIATE class and assign to a variable: $obj = new CustomerDTO(); * FOR EACH (can be many) needed property use the variable with object instance and assign the property value: $obj->property = $value; Now when you deal a lot with objects like that you can see you're constantly repeating yourself with $obj-> part all the lines down before you probably would use that instance. There is no need for the constructor in such objects, they're purpose is to transfer data between app layers. Last months I've been working on a project which often requires to create an entity with data decoded from JSON format, most of them had many fields|properties required and only some of them were optional (even those optional were nullable so possibly I could initialize them with null once again), so the case with which I had a lot to do was creating factories which instantiate and for each property initialize value just to be able to return newly created instance in the end. That's when I thought object initializer could reduce a lot of boilerplate. That's when I started thinking of many places where I could use that, where my IDE could help me to write code faster without all that noise of constantly repeating myself. The syntax is similar to other languages. The narrow case described in BC changes is already deprecated and in my opinion, probably even not used or very rare. The future scope features can potentially save even more strikes but that's not the main reason about that RFC - nice to have but let's start with something. Regards, Michał Brzuchalski
  107041
September 13, 2019 05:49 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Lynn,

czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com> napisał(a):

> Heya, > > What's the added benefit of this compared to implementing a constructor? > > The part I like is that this can be used to replace stdClass/structured > arrays. Perhaps something like this would nice to have in PHP: > > ``` > $people = []; > > foreach ($peopleFromDatabase as [$id, $username, $name]) { > $people[] = { > Uuid id => $id, > string username => $username, > string name => $name, > }; > // and possible automatic assignment: > $people[] = {Uuid $id, string $username, string $name}; > } > ``` >
Removing stdClass for instantiation and initialization of simple objects is one of a future scope proposal. This RFC tries to address instantiation and initialization boilerplate reduction with a syntax which would not be restricted to stdClass only. Although it's not a game-changer, simple addition to the language which reduces boilerplate when dealing with objects which don't need complex constructors like for eg. DTO objects.. Regards,
> Lynn van der Berg > >
Regards, Michał Brzuchalski
  107043
September 13, 2019 06:39 claude.pache@gmail.com (Claude Pache)
> Le 13 sept. 2019 à 07:49, Michał Brzuchalski brzuchalski@gmail.com> a écrit : > > Hi Lynn, > > czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com <mailto:kjarli@gmail.com>> napisał(a): > >> Heya, >> >> What's the added benefit of this compared to implementing a constructor? >> >> The part I like is that this can be used to replace stdClass/structured >> arrays. Perhaps something like this would nice to have in PHP: >> >> ``` >> $people = []; >> >> foreach ($peopleFromDatabase as [$id, $username, $name]) { >> $people[] = { >> Uuid id => $id, >> string username => $username, >> string name => $name, >> }; >> // and possible automatic assignment: >> $people[] = {Uuid $id, string $username, string $name}; >> } >> ``` >> > > Removing stdClass for instantiation and initialization of simple objects is > one of a future scope proposal. > > This RFC tries to address instantiation and initialization boilerplate > reduction with a syntax which would > not be restricted to stdClass only. > > Although it's not a game-changer, simple addition to the language which > reduces boilerplate when dealing > with objects which don't need complex constructors like for eg. DTO objects. >
As for `stdClass`, PHP has already a syntax: $baz = "baz"; $obj = (object) [ "foo" => "bar", $baz => true ]; For other type of objects, that could be done with a simple helper function $customer = object_assign(new Customer, [ "id" => 123, "name" => "John Doe", ]); where: function object_assign(object $obj, iterable $data): object { foreach ($data as $key => $value) { $obj->$key = $value; } return $obj; } That said, I generally use arrays rather than DTO objects or such, so that I can’t speak from experience. —Claude
  107045
September 13, 2019 07:15 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Claude,

pt., 13 wrz 2019 o 08:39 Claude Pache pache@gmail.com> napisał(a):

> > > Le 13 sept. 2019 à 07:49, Michał Brzuchalski brzuchalski@gmail.com> > a écrit : > > Hi Lynn, > > czw., 12 wrz 2019 o 17:01 Lynn <kjarli@gmail.com> napisał(a): > > Heya, > > What's the added benefit of this compared to implementing a constructor? > > The part I like is that this can be used to replace stdClass/structured > arrays. Perhaps something like this would nice to have in PHP: > > ``` > $people = []; > > foreach ($peopleFromDatabase as [$id, $username, $name]) { > $people[] = { > Uuid id => $id, > string username => $username, > string name => $name, > }; > // and possible automatic assignment: > $people[] = {Uuid $id, string $username, string $name}; > } > ``` > > > Removing stdClass for instantiation and initialization of simple objects is > one of a future scope proposal. > > This RFC tries to address instantiation and initialization boilerplate > reduction with a syntax which would > not be restricted to stdClass only. > > Although it's not a game-changer, simple addition to the language which > reduces boilerplate when dealing > with objects which don't need complex constructors like for eg. DTO > objects. > > > As for `stdClass`, PHP has already a syntax: > > $baz = "baz"; > $obj = (object) [ > "foo" => "bar", > $baz => true > ]; > > For other type of objects, that could be done with a simple helper function > > $customer = object_assign(new Customer, [ > "id" => 123, > "name" => "John Doe", > ]); > > where: > > function object_assign(object $obj, iterable $data): object { > foreach ($data as $key => $value) { > $obj->$key = $value; > } > return $obj; > } > > That said, I generally use arrays rather than DTO objects or such, so that > I can’t speak from experience. > > —Claude > > You're of course right this can be done more or less with a helper function
which does all the required type validation through reflection, but I believe that feature requests for language support which reduces boilerplate can easily spread that simplification and bring all the required type check validations out of the box, right? IMO the thing is as you said this would require a helper function and not all simply like them! Regards, Michał Brzuchalski
  107047
September 13, 2019 07:35 mike@newclarity.net (Mike Schinkel)
> That said, I generally use arrays rather than DTO objects
I too have often used arrays. But then I have always been rigorous to ensure that variables and array elements are initialized before use, and fortunately I do not have a lot of typos in code I write. However I recently took on a large PHP app to refactor where the previous developers had used arrays profusely with very little pre-initialization. So I started to add in all initialization and checking to be certain variables had been initialized and realized it was an overwhelming task. I changed course and am now replacing all all arrays that are emulating a struct (vs a collection) to instances of declared classes and it has made a major improvement in my ability to refactor without bloating the code to 2-3x the original size. I has also allowed me to consolidate lots of repeated code in one place in methods and constructors. Given that experience and knowing that using "args" for optional parameters means my function signatures are much less likely to change, it would be super helpful if I could do so with type-checked object initializers instead of array literals. -Mike
  107059
September 13, 2019 08:51 arnold.adaniels.nl@gmail.com (Arnold Daniels)
This can easily be done in a number of ways, like the suggested helper
function. The same can be said for many other features that were
implemented recently, like array unpacking. This feature is easy to
implement and will make the code for data objects much more readable, with
additional benefits for static analysis.

Comments:

I don't like how it works for anonymous classes. It's more difficult to
implement since the compiler doesn't know the meaning of the (first)
bracket. It's doesn't make the code more readable, for the same reason. I
think it's better to not support this syntax with anonymous classes.

The examples do not show how constructor arguments are passed. I'm assuming
it's

    $customer = new Customer("foo") {
        name = "John"
    };

About the idea of letting `{ foo = 10 }` create an `stdClass` object (not
in the RFC); While not used much since it has no effect, it's perfectly
okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I
don't think it's a good idea to allow `new stdClass` to be omitted.
  107063
September 13, 2019 09:04 mike@newclarity.net (Mike Schinkel)
Arnold,

> This can easily be done in a number of ways, like the suggested helper > function.
A helper function can simulate an object initializer but it cannot provide the same potential benefits. The helper function uses array keys to identify properties, and as array keys cannot be declared like class properties, neither IDE, tools, nor PHP itself can validate the names and types of those keys like they could if object initializers were used instead. Michal,
> I don't like how it works for anonymous classes. It's more difficult to > implement since the compiler doesn't know the meaning of the (first) > bracket. It's doesn't make the code more readable, for the same reason. I > think it's better to not support this syntax with anonymous classes. > > The examples do not show how constructor arguments are passed. I'm assuming > it's > > $customer = new Customer("foo") { > name = "John" > }; > > About the idea of letting `{ foo = 10 }` create an `stdClass` object (not > in the RFC); While not used much since it has no effect, it's perfectly > okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I > don't think it's a good idea to allow `new stdClass` to be omitted.
I am curious why your RFC uses "=" instead of "=>" for separating properties from values? If we used "=>" it is potential — with lookahead parsing — that the following could be unambiguous: { foo => 10 } -Mike
  107067
September 13, 2019 09:22 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Mike,

I am curious why your RFC uses "=" instead of "=>" for separating
> properties from values? > > If we used "=>" it is potential — with lookahead parsing — that the > following could be unambiguous: > > { foo => 10 } > > The reason about choosing "=" was a simplification of instantiation and
properties initialisation and we use "=" to assign property values now. The "=>" is specific for array key pairs and IMO should stay specific for arrays only. Thanks, Michał Brzuchalski
  107068
September 13, 2019 09:29 mike@newclarity.net (Mike Schinkel)
> IMO should stay specific for arrays only.
Why? Is there an objective reason? Is there some aspect of the syntax that makes it appropriate for arrays but inappropriate for object initialization? These are honest question. My two cents: I would like to type less too, but I have always assumed that "=>" was the PHP-ish syntax for initialization of a structure so it seems very appropriate to use for object initialization too. That said, 1. I don't feel strongly about iti either way, 2. Unless using "=>" would make naked object initialization possible — i.e. { foo => 10 } — given that it seems, as Arnold said, this would conflict with other meanings: { foo = 10 } #fwiw -Mike
  107070
September 13, 2019 09:39 michal.brzuchalski@gmail.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
pt., 13 wrz 2019 o 11:29 Mike Schinkel <mike@newclarity.net> napisał(a):

> IMO should stay specific for arrays only. > > > Why? Is there an objective reason? > > $obj->foo => 123;
Simply, that's not the way you initialize object property values. When you use "=>" in array context you're pushing a new value to array and it'll work without labelling it with key where the key could be number index or string index in what shape you want (whitespaces etc.).
> Is there some aspect of the syntax that makes it appropriate for arrays > but inappropriate for object initialization? > > > These are honest question. > > > My two cents: I would like to type less too, but I have always assumed > that "=>" was the PHP-ish syntax for initialization of a structure so it > seems very appropriate to use for object initialization too. > > > That said, > > > 1. I don't feel strongly about iti either way, > > 2. Unless using "=>" would make naked object initialization possible — > i.e. { foo => 10 } — given that it seems, as Arnold said, this would > conflict with other meanings: { foo = 10 } > > > { $foo = 123 }; // unexpected "}" cause of missing ";"
$bar = { $foo = 123 }; // unexpected "{" cause it's not allowed in this context Both examples are syntax error. You can use {} for separating blocks of code, but now if you wanna assign value. Everything considered syntax error can be used for feature shaping. Regards, Michał Brzuchalski
  107072
September 13, 2019 09:47 mike@newclarity.net (Mike Schinkel)
> On Sep 13, 2019, at 2:39 AM, Michał Brzuchalski brzuchalski@gmail.com> wrote: > $obj->foo => 123; > Simply, that's not the way you initialize object property values.
So by the same logic this should be PHP's way to initialize array elements, right? $arr['foo'] => 123; But it is not. Obviously we both know that this is the correct syntax: $arr['foo'] = 123; So that is why I think it would be more consistent in PHP for object initializers to use "=>" instead of "=." But, I myself will not belabor the point beyond this message. Either others will agree with you or they will suggest to change to "=>" too.
> { $foo = 123 }; // unexpected "}" cause of missing ";" > $bar = { $foo = 123 }; // unexpected "{" cause it's not allowed in this context > Both examples are syntax error. > You can use {} for separating blocks of code, but now if you wanna assign value. > Everything considered syntax error can be used for feature shaping.
Hmm. Ok, I will let Arnold reply to you on this if he feels that your reply did not address his concerns. -Mike
  107065
September 13, 2019 09:19 michal@brzuchalski.com (=?UTF-8?Q?Micha=C5=82_Brzuchalski?=)
Hi Arnold,

pt., 13 wrz 2019 o 10:51 Arnold Daniels nl@gmail.com>
napisał(a):

> This can easily be done in a number of ways, like the suggested helper > function. The same can be said for many other features that were > implemented recently, like array unpacking. This feature is easy to > implement and will make the code for data objects much more readable, with > additional benefits for static analysis. > > Comments: > > I don't like how it works for anonymous classes. It's more difficult to > implement since the compiler doesn't know the meaning of the (first) > bracket. It's doesn't make the code more readable, for the same reason. I > think it's better to not support this syntax with anonymous classes. > > Probably lexical scope for anon classes would be better here, but due to
fact that proposal is to use initializer block instead of constructor arguments, that was the reason why initializer block got before anon class definitions.
> The examples do not show how constructor arguments are passed. I'm assuming > it's > > $customer = new Customer("foo") { > name = "John" > }; > > The examples don't show that cause it's forbidden.
There is a note on that in RFC on purpose
> Note! Object instantiation allows only constructors without required arguments to be used.
> Any class which requires passing arguments to constructor cannot be used in combination with object initializer.
Using constructor arguments and object initializer would introduce noise and you'll be potentially initializing object twice: using object initializer block and using constructor args what may be misleading.
> About the idea of letting `{ foo = 10 }` create an `stdClass` object (not > in the RFC); While not used much since it has no effect, it's perfectly > okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I > don't think it's a good idea to allow `new stdClass` to be omitted. >
Future scope mentions only about letting to create stdClass with omitting of the class name only, nothing said about removing a new keyword. Thanks, Michał Brzuchalski
  107075
September 13, 2019 11:26 kjarli@gmail.com (Lynn)
On Fri, Sep 13, 2019 at 10:51 AM Arnold Daniels <
arnold.adaniels.nl@gmail.com> wrote:

> Comments: > > I don't like how it works for anonymous classes. It's more difficult to > implement since the compiler doesn't know the meaning of the (first) > bracket. It's doesn't make the code more readable, for the same reason. I > think it's better to not support this syntax with anonymous classes. > > The examples do not show how constructor arguments are passed. I'm assuming > it's > > $customer = new Customer("foo") { > name = "John" > }; > > About the idea of letting `{ foo = 10 }` create an `stdClass` object (not > in the RFC); While not used much since it has no effect, it's perfectly > okay to put your code in brackets eg `{ { { $foo = 10; } } }`. As such, I > don't think it's a good idea to allow `new stdClass` to be omitted. >
If the compiler implementation of `$foo = {...};` would give issues, `$foo = new {...};` could be an alternative for run-time declared anonymous classes (preferably with typed properties). In regards of stdClass, I don't think this should be used. Regards, Lynn van der Berg
  107046
September 13, 2019 07:22 mike@newclarity.net (Mike Schinkel)
> What's the added benefit of this compared to implementing a constructor?
Michal my have a different answer but my answer are the following two benefits plus a 3rd related benefit: 1. The ability to add strictness to object initialization vs. using arrays of properties. PHP itself could flag misspelled or missing properties with a warning or an error which is something PHP cannot really do given that array keys are strings and not identifiers. 2. The ability for tools like PhpStorm, PHStan and others to more easily identify and flag these errors. It is hard (impossible?) for a tool to validate array element keys because there is no way to declare named and typed array elements keys in PHP. 3. It would empowering developers to pass optional elements to functions and methods that can also be type checked. The 2nd example would allow type checking but the first would not: EXAMPLE 1 function foo( int $id, array $args ) { ... } foo( 1, array( "bar" => "abc", "baz" => 123, } EXAMPLE 2 class FooArgs { public string $bar public int $baz } function foo( int $id, FooArgs $args ) { ... } foo( 1, FooArgs{ bar = "abc", baz = 123, } -Mike
> The part I like is that this can be used to replace stdClass/structured > arrays. Perhaps something like this would nice to have in PHP: > > ``` > $people = []; > > foreach ($peopleFromDatabase as [$id, $username, $name]) { > $people[] = { > Uuid id => $id, > string username => $username, > string name => $name, > }; > // and possible automatic assignment: > $people[] = {Uuid $id, string $username, string $name}; > } > ``` > > Regards, > Lynn van der Berg