rfc:compact-object-property-assignment
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
rfc:compact-object-property-assignment [2020/03/22 03:17] – jgivoni | rfc:compact-object-property-assignment [2020/03/31 02:16] – jgivoni | ||
---|---|---|---|
Line 16: | Line 16: | ||
This //**pseudo object literal**// notation, (though not limited to such use) is intended to enable the developer to //**create an object and populating it inline**//, f.ex. directly as an argument in a function call. | This //**pseudo object literal**// notation, (though not limited to such use) is intended to enable the developer to //**create an object and populating it inline**//, f.ex. directly as an argument in a function call. | ||
- | As an alternative to writing a data structure as an associative array, COPA gives the data a **// | + | As an alternative to writing a data structure as an associative array, COPA gives the data a **// |
- | You may find that COPA is best suited when you don’t mind public properties, don’t require | + | > COPA does not introduce any new concepts or complexities, but merely |
==== Example ==== | ==== Example ==== | ||
Line 27: | Line 27: | ||
<code php> | <code php> | ||
- | $myObj = new Foo(); // 1. Create | + | $myObj-> |
- | + | ||
- | $myObj-> | + | |
$myObj-> | $myObj-> | ||
$myObj-> | $myObj-> | ||
- | |||
- | doTheFoo($myObj); | ||
</ | </ | ||
=== You will be able to do this: === | === You will be able to do this: === | ||
<code php> | <code php> | ||
- | doTheFoo((new Foo)->[ | + | $myObj->[ |
a = 1, | a = 1, | ||
b = 2, | b = 2, | ||
c = 3, | c = 3, | ||
- | ]); | + | ]; |
</ | </ | ||
- | COPA is everything that comes after the object expression. You can use COPA on any expression that evaluates to an object. COPA is not tied to object construction, | + | > COPA is everything that comes after the object expression. You can use COPA on any expression that evaluates to an object. COPA is not tied to object construction, |
- | //See more use cases below - and keep an open mind about the syntax; the options are limited these days :-)// | + | //See more use cases below.// |
==== Motivation ==== | ==== Motivation ==== | ||
Line 62: | Line 58: | ||
* **Simple** - does what you would expect without introducing any new concepts into the language | * **Simple** - does what you would expect without introducing any new concepts into the language | ||
- | //My focus is to find a **pragmatic** solution that can be implemented soon, but won’t | + | > My focus is to find a **pragmatic** solution that is trivial to implement, and won’t |
- | //If you have ever wanted to create, populate and send an object inside a function call, COPA is your chance!// | + | //If you have ever wanted to create, populate and send an object inside a function call, COPA is for you!// |
===== Proposal ===== | ===== Proposal ===== | ||
Line 78: | Line 74: | ||
] | ] | ||
</ | </ | ||
- | A trailing comma is permitted for the same reasons it's permitted in array literals and [[https:// | + | > A trailing comma is permitted for the same reasons it's permitted in array literals and [[https:// |
The whole block is considered an expression that returns the object before the arrow. | The whole block is considered an expression that returns the object before the arrow. | ||
- | This syntax was chosen for its availability in the language. If we land on another syntax, I’m not married to this one. The only criteria are that it doesn’t conflict with anything else, that it is brief and feels good. | + | This syntax was chosen for its availability in the language. If we land on another syntax, I’m not married to this one. The only criteria are that it doesn’t conflict with anything else, that it is trivial to implement, |
==== Interpretation ==== | ==== Interpretation ==== | ||
Line 104: | Line 100: | ||
==== Use cases ==== | ==== Use cases ==== | ||
- | === Alternative to passive associative | + | === Create and send struct === |
+ | |||
+ | <code php> | ||
+ | // Instead of this: | ||
+ | |||
+ | $myObj = new Foo; // 1. Create struct-like object without constructor arguments | ||
+ | |||
+ | $myObj-> | ||
+ | $myObj-> | ||
+ | $myObj-> | ||
+ | |||
+ | doTheFoo($myObj); | ||
+ | |||
+ | // Use COPA: | ||
+ | |||
+ | doTheFoo((new Foo)-> | ||
+ | a = 1, | ||
+ | b = 2, | ||
+ | c = 3, | ||
+ | ]); | ||
+ | </ | ||
+ | //No boilerplate needed.// | ||
+ | |||
+ | === Stop using arrays === | ||
<code php> | <code php> | ||
Line 110: | Line 129: | ||
doSomething([ | doSomething([ | ||
- | ' | + | ' |
' | ' | ||
]); | ]); | ||
Line 116: | Line 135: | ||
// Use COPA: | // Use COPA: | ||
- | class Options { | + | class Options { // Give the data a signature, a well-defined structure |
public $a; | public $a; | ||
public $b; | public $b; | ||
Line 128: | Line 147: | ||
//If you often create, populate and send the same families of data structure, declaring those structures and using COPA makes it a breeze.// | //If you often create, populate and send the same families of data structure, declaring those structures and using COPA makes it a breeze.// | ||
- | === DTOs - data transfer objects | + | === Nested COPA === |
- | + | ||
- | Typical characteristics of DTOs: | + | |
- | + | ||
- | * many properties | + | |
- | * properties may be optional, with default values | + | |
- | * public visibility on properties, i.e. no desire for boilerplate code to create setters and getters for each one | + | |
- | * desirability to create, populate and send in one go | + | |
- | == With current syntax == | + | COPA is not limited to a flat structure. |
<code php> | <code php> | ||
- | class FooDto { | + | (new Foo)->[ |
- | public ?string $mane = null; | + | |
- | public int $padme = 1; // Optional, with default | + | mane = 'a', |
- | public ?FooDto $hum = null; | + | hum = (new Foo)->[ |
- | } | + | |
- | + | ||
- | $foo = new FooDto(); // Instantiating the object first | + | |
- | $foo->mane = ' | + | |
- | // Skipping the $padme property which has a default value | + | |
- | $foo-> | + | |
- | $foo->hum-> | + | |
- | + | ||
- | doTheFoo($foo); | + | |
- | </ | + | |
- | == With new COPA syntax == | + | |
- | + | ||
- | <code php> | + | |
- | doTheFoo((new FooDto)-> | + | |
- | mane = 'get', | + | |
- | hum = (new FooDto)-> | + | |
mane = ' | mane = ' | ||
], | ], | ||
- | ]); | + | ]; |
</ | </ | ||
- | //Though the example is not a typical DTO, it represents the characteristics.// | + | === Split options from services === |
- | === Argument bags === | + | Separate concerns |
- | + | ||
- | Argument bags are typically used when: | + | |
- | + | ||
- | * many arguments needs to be passed to a function | + | |
- | * some arguments are optional | + | |
- | * order of arguments is not important | + | |
- | + | ||
- | With the proposed new syntax we can **avoid using simple arrays** | + | |
- | + | ||
- | == With current syntax == | + | |
- | + | ||
- | <code php> | + | |
- | class Foo { | + | |
- | protected string $mane; | + | |
- | protected int $padme; | + | |
- | protected string $hum; | + | |
- | + | ||
- | public function __construct(array $options) { | + | |
- | $this->mane = $options[' | + | |
- | $this-> | + | |
- | $this-> | + | |
- | } | + | |
- | } | + | |
- | + | ||
- | $myFoo = new Foo([ | + | |
- | ' | + | |
- | ' | + | |
- | ]); | + | |
- | </ | + | |
- | == With new COPA syntax == | + | |
<code php> | <code php> | ||
- | class FooOptions { // Separate concerns into an options class that handles optional and default values... | + | class FooOptions { |
public ?string $mane = null; | public ?string $mane = null; | ||
public int $padme = 1; // Optional, with default | public int $padme = 1; // Optional, with default | ||
Line 205: | Line 171: | ||
} | } | ||
- | class Foo { // And the main class that receives the options and handles some feature | + | class Foo { |
protected FooOptions $options; | protected FooOptions $options; | ||
public function __construct(FooOptions $options) { | public function __construct(FooOptions $options) { | ||
// Do some validate here if you must, f.ex. checking for mandatory parameters | // Do some validate here if you must, f.ex. checking for mandatory parameters | ||
- | $this-> | + | $this-> |
} | } | ||
} | } | ||
- | $myFoo = new Foo((new FooOptions)-> | + | $myFoo = new Foo((new FooOptions)-> |
- | mane = ' | + | mane = ' |
hum = ' | hum = ' | ||
]); | ]); | ||
</ | </ | ||
- | //The other alternative | + | //If you can’t wait for “named parameters” and often resort |
==== Special cases ==== | ==== Special cases ==== | ||
Line 237: | Line 203: | ||
var_dump($myObj-> | var_dump($myObj-> | ||
</ | </ | ||
- | //As the assignments are carried out in order on the object, you can use the new value of a previous assigment in a following one.// | + | > As the assignments are carried out in order on the object, you can use the new value of a previous assigment in a following one. |
- | + | ||
- | //There may be arguments equally for and against this behavior, but ultimately the simplicity of the design and implementation wins, in my opinion.// | + | |
=== Exceptions === | === Exceptions === | ||
Line 261: | Line 225: | ||
<code php> | <code php> | ||
- | // With COPA | + | // With COPA: |
try { | try { | ||
$foo->[ | $foo->[ | ||
Line 274: | Line 239: | ||
</ | </ | ||
<code php> | <code php> | ||
- | // Without COPA | + | // Without COPA: |
try { | try { | ||
$foo-> | $foo-> | ||
Line 295: | Line 261: | ||
The result in all cases is that '' | The result in all cases is that '' | ||
- | < | + | < |
object(Foo)# | object(Foo)# | ||
[" | [" | ||
Line 305: | Line 271: | ||
} | } | ||
</ | </ | ||
- | // | + | > COPA is **not** an atomic operation in the same way that method chaining isn’t. |
==== Out of scope / future scope ==== | ==== Out of scope / future scope ==== | ||
- | This section contains | + | This section contains features that is not considered for implementation in version 1 of COPA but may be considered later. |
- | === Can you do that? === | + | === You can’t |
- | The following examples show some things that is now possible using regular property accessor, | + | The following examples show various |
<code php> | <code php> | ||
Line 331: | Line 297: | ||
]; | ]; | ||
</ | </ | ||
- | //If anyone | + | //These can be implemented in the future if there is a demand.// |
- | === Nested COPA === | + | === Nested COPA on existing objects |
- | It might be nice to be able to populate an existing nested object | + | The following syntax could be supported |
<code php> | <code php> | ||
// This example, using current syntax... | // This example, using current syntax... | ||
+ | |||
$foo->a = 1; | $foo->a = 1; | ||
$foo-> | $foo-> | ||
// Could be written with COPA like this: | // Could be written with COPA like this: | ||
+ | |||
$foo->[ | $foo->[ | ||
a = 1, | a = 1, | ||
Line 351: | Line 319: | ||
// But for now you'll have to do this: | // But for now you'll have to do this: | ||
+ | |||
$foo->[ | $foo->[ | ||
a = 1, | a = 1, | ||
Line 361: | Line 330: | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== | ||
- | None. Array followed by square bracket causes syntax error in PHP 7.4. This new syntax is optional. If you don't use it, your code will continue to run. | + | None. |
+ | |||
+ | > **Note!** | ||
===== Proposed PHP Version(s) ===== | ===== Proposed PHP Version(s) ===== | ||
Line 371: | Line 342: | ||
==== Alternative syntaxes ==== | ==== Alternative syntaxes ==== | ||
- | I’m going to suggest some alternative syntaxes, | + | I’m going to suggest some alternative syntaxes, |
=== Syntax A === | === Syntax A === | ||
Line 401: | Line 372: | ||
}; | }; | ||
</ | </ | ||
+ | > Going from deprecation in 7.4 to removal of support in 8.0 may is not unprecedented. Old code that has not been mended won’t silently do something spurious. | ||
+ | |||
=== Syntax C === | === Syntax C === | ||
Line 416: | Line 389: | ||
</ | </ | ||
Nesting becomes awkward - how do we jump out again? | Nesting becomes awkward - how do we jump out again? | ||
+ | |||
+ | > **Note!** This looks more like a chain of normal assignments, | ||
=== Syntax D === | === Syntax D === | ||
- | Repeating the array for familiarity: | + | Repeating the arrow for familiarity |
<code php> | <code php> | ||
Line 431: | Line 406: | ||
; | ; | ||
</ | </ | ||
- | Same issue with nested | + | Same issues |
=== Syntax E === | === Syntax E === | ||
Line 447: | Line 422: | ||
); | ); | ||
</ | </ | ||
+ | === Syntax F === | ||
+ | |||
+ | **WITH** syntax | ||
+ | |||
+ | <code php> | ||
+ | myObj.with { | ||
+ | foo = 10 | ||
+ | bar = foo + 20 | ||
+ | } | ||
+ | </ | ||
+ | If this is the preferred syntax it will require a new RFC. | ||
+ | |||
+ | ===== Rejected Features ===== | ||
+ | |||
+ | Some suggested features have been rejected due to the fact that COPA aims to be pragmatic, with a trivial implementation and without introducing any new concepts to avoid a combinatorial explosion of complexities in the future. | ||
+ | |||
==== Mandatory properties ==== | ==== Mandatory properties ==== | ||
- | Some criticism | + | Some have voiced |
- | Rowan Tommins: | + | **Rowan Tommins:** |
> It seems pretty rare that an object would have no mandatory properties, so saying “if you have a mandatory property, COPA is not for you” is ruling out a lot of uses. | > It seems pretty rare that an object would have no mandatory properties, so saying “if you have a mandatory property, COPA is not for you” is ruling out a lot of uses. | ||
- | Michał Brzuchalski: | + | **Michał Brzuchalski: |
> This helps to avoid bugs where a property is added to the class but forgot to be assigned it a value in all cases where the class is instantiated and initialized | > This helps to avoid bugs where a property is added to the class but forgot to be assigned it a value in all cases where the class is instantiated and initialized | ||
Line 461: | Line 452: | ||
Mandatory properties are typed properties without a default value. They are in the uninitialized state until they are assigned a value. It has been suggested that an exception should be thrown at the end of the constructor if any property is still uninitialized, | Mandatory properties are typed properties without a default value. They are in the uninitialized state until they are assigned a value. It has been suggested that an exception should be thrown at the end of the constructor if any property is still uninitialized, | ||
- | For now you must continue to write your own validation code to be carried out at the appropriate “point of no return”. | + | > COPA won’t support this since COPA doesn’t introduce any new concepts or complexities. The lack of this feature is not a limitation of COPA when compared to current functionality. |
+ | |||
+ | //For now you must continue to write your own validation code to be carried out at the appropriate “point of no return”.// | ||
==== Atomic operations ==== | ==== Atomic operations ==== | ||
- | It’s also been suggested that assigning multiple values using COPA should be an atomic operation that either succeeds or fails in its entirety. | + | It’s also been suggested that assigning multiple values using COPA should be an atomic operation that either succeeds or fails in its entirety |
- | That does sound cool as well, and may seem like the expected behavior for some. | + | Though that sounds |
- | Still, I’m not convinved it’s worth the extra hassle, since what were you planning to do with the incomplete object anyway? | + | > **Note!** Chaining method calls is not an atomic operation either. The cost/ |
- | Chaining method calls are not an atomic operation and if an exception is thrown in the middle I doubt you would raise an eyebrow about the previous call having altered the state of the object. | + | ===== Vote ===== |
- | ===== Proposed | + | Voting |
The primary vote of whether or not to accept this RFC requires a 2/3 majority. | The primary vote of whether or not to accept this RFC requires a 2/3 majority. | ||
- | A secondary “vote” directed at no-voters, will ask you the primary reason | + | <doodle title=" |
- | + | * Yes | |
- | The options will be: | + | * No |
+ | </ | ||
- | | + | <doodle title=" |
- | | + | |
- | | + | * I don’t find the feature useful |
- | | + | * I don’t like the syntax |
- | | + | * I prefer a more comprehensive solution to this problem |
- | | + | * I prefer a narrower solution to this problem |
- | | + | * This breaks backwards compatibility |
- | | + | * This will have negative implications for future |
- | | + | * This will be a nightmare to implement and maintain |
+ | * I prefer not to say | ||
+ | </ | ||
- | This will help understand what the obstacles are, when studying this RFC in the future, should anyone be tempted to have another shot at object literals et. al. | + | <doodle title=" |
+ | * A (the proposed one) | ||
+ | * B | ||
+ | * C | ||
+ | * D | ||
+ | * E | ||
+ | * F | ||
+ | * Irrelevant | ||
+ | </ | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== |
rfc/compact-object-property-assignment.txt · Last modified: 2020/04/14 06:30 by jgivoni