rfc:compact-object-property-assignment

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
rfc:compact-object-property-assignment [2020/03/19 02:35] jgivonirfc:compact-object-property-assignment [2020/04/14 06:30] (current) jgivoni
Line 3: Line 3:
 **COPA: A //pragmatic// approach to object literals** **COPA: A //pragmatic// approach to object literals**
  
-  * Version: 1.2+  * Version: 1.3
   * Date: 2020-03-17   * Date: 2020-03-17
   * Author: Jakob Givoni <jakob@givoni.dk>   * Author: Jakob Givoni <jakob@givoni.dk>
-  * Status: Under Discussion +  * Status: Declined 
-  * Discussion: https:%%//%%externals.io/message/109055+  * Discussion: [[https://externals.io/message/109055]]
  
 ===== Introduction ===== ===== Introduction =====
Line 17: Line 17:
 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 **//documented signature//** so that you know what parameters are expected and their values.+As an alternative to writing a data structure as an associative array, COPA gives the data a **//documented signature//** so that you know what parameters are expected and their value types.
  
-You may find that COPA is best suited when you don’t mind public propertiesdon’t require constructor and is willing to compromise on magic enforcement of mandatory properties, since as [[https://wiki.php.net/rfc/write_once_properties#read-only_semantics|the saying goes]]: //Object construction is a fuzzy concept in PHP and lazy initialisation is a feature!//+COPA does not introduce any new concepts or complexitiesbut merely new syntax aimed at making millions of PHP developers write their code in a simpler wayThe code becomes easier to write and read and thus more maintainable without any lateral limitations or factual downsides.
  
 ==== Example ==== ==== Example ====
Line 28: Line 28:
  
 <code php> <code php>
-$myObj = new Foo(); // 1. Create +$myObj->a = 1;
- +
-$myObj->a = 1; // 2. Populate+
 $myObj->b = 2; $myObj->b = 2;
 $myObj->c = 3; $myObj->c = 3;
- 
-doTheFoo($myObj); // 3. Send 
 </code> </code>
 === 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,
-]);+];
 </code> </code>
-//See more use cases below - and keep an open mind about the syntax; the options are limited these days :-)//+> 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, but can be used anytime, anywhere in the objects life, as many times as you want. 
 + 
 +//See more use cases below.//
  
 ==== Motivation ==== ==== Motivation ====
Line 61: Line 59:
   * **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 soonbut won’t trip up the continuing, inspiring development of the awesome language that is PHP.//+My focus is to find a **pragmatic** solution that is trivial to implementand won’t impede futher development of the language.
  
-//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 77: Line 75:
 ] ]
 </code> </code>
-A trailing comma is permitted for the same reasons it's permitted in array literals and [[https://wiki.php.net/rfc/trailing-comma-function-calls|function calls (as of PHP 7.3)]].+A trailing comma is permitted for the same reasons it's permitted in array literals and [[https://wiki.php.net/rfc/trailing-comma-function-calls|function calls (as of PHP 7.3)]].
  
 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, brief and feels ok.
  
 ==== Interpretation ==== ==== Interpretation ====
Line 103: Line 101:
 ==== Use cases ==== ==== Use cases ====
  
-=== Alternative to passive associative arrays ===+=== Create and send struct === 
 + 
 +<code php> 
 +// Instead of this: 
 + 
 +$myObj = new Foo; // 1. Create struct-like object without constructor arguments 
 + 
 +$myObj->a = 1; // 2. Populate public properties 
 +$myObj->b = 2; 
 +$myObj->c = 3; 
 + 
 +doTheFoo($myObj); // 3. Send or process 
 + 
 +// Use COPA: 
 + 
 +doTheFoo((new Foo)->
 +    a = 1, 
 +    b = 2, 
 +    c = 3, 
 +]); 
 +</code> 
 +//No boilerplate needed.// 
 + 
 +=== Stop using arrays ===
  
 <code php> <code php>
Line 109: Line 130:
  
 doSomething([ doSomething([
-    'a' => 1, // Array syntax doesn't provide any help on parameter names+    'a' => 1, // Anonymous array doesn't provide any help on parameter names
     'b' => 2, // or types     'b' => 2, // or types
 ]); ]);
Line 115: Line 136:
 // 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 127: Line 148:
 //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: +COPA is not limited to a flat structure.
- +
-  * 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 ==+
  
 <code php> <code php>
-class FooDto { +(new Foo)->
-    public ?string $mane = null; +    om = 'get', 
-    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 = 'get'; // Setting the properties +
-// Skipping the $padme property which has a default value +
-$foo->hum = new FooDto(); // Creating a nested DTO +
-$foo->hum->mane = 'life'; // Populating the nested DTO +
- +
-doTheFoo($foo); // Passing it to function +
-</code> +
-== With new COPA syntax == +
- +
-<code php> +
-doTheFoo((new FooDto)->[ // Constructing and populating inline +
-    mane = 'get', +
-    hum = (new FooDto)->// Even nested structures+
         mane = 'life',         mane = 'life',
     ],     ],
-]);+];
 </code> </code>
-//Though the example is not a typical DTO, it represents the characteristics.//+=== Split options from services ===
  
-=== Argument bags === +Separate concerns and use composition. In this example, once you have instantiated Foo, the options are no longer writeableeven though the options were public properties.
- +
-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** and instead get **autocomplete** and **type-checking** in the IDE with a syntax that smells of **named parameters**: +
- +
-== With current syntax == +
- +
-<code php> +
-class Foo { +
-    protected string $mane; +
-    protected int $padme; +
-    protected string $hum; +
- +
-    public function __construct(array $options) { +
-        $this->mane = $options['mane']; +
-        $this->padme = $options['padme'] ?? 1; +
-        $this->hum = $options['hum']; +
-    } +
-+
- +
-$myFoo = new Foo([ +
-    'mane' => 'get'// Array syntax doesn't provide any help on parameter names +
-    'hum' => 'life'// or types +
-]); +
-</code> +
-== 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 204: Line 172:
 } }
  
-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->options = $options;+        $this->options = clone $options;
     }     }
 } }
  
-$myFoo = new Foo((new FooOptions)->// Objects as argument bags (pseudo named parameters?) +$myFoo = new Foo((new FooOptions)->
-    mane = 'get', // Parameter name and type checking+    mane = 'get',
     hum = 'life',     hum = 'life',
 ]); ]);
 </code> </code>
-//The other alternative to an argument bag is usually constructor with many arguments, which is something that has been attempted to solve with RFCs arguing for automatic promotion of arguments to properties (f.ex. [[https://wiki.php.net/rfc/automatic_property_initialization|RFC: Automatic Property Initialization]], but which is probably also better left to the COPA argument bag example above.//+//If you can’t wait for “named parameters” and often resort to “parameter bags” this is a perfectly valid and saner alternative.//
  
 ==== Special cases ==== ==== Special cases ====
Line 236: Line 204:
 var_dump($myObj->bar); // int(30) var_dump($myObj->bar); // int(30)
 </code> </code>
-//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 260: Line 226:
  
 <code php> <code php>
-// With COPA+// With COPA
 try { try {
     $foo->[     $foo->[
Line 273: Line 240:
 </code> </code>
 <code php> <code php>
-// Without COPA+// Without COPA
 try { try {
     $foo->setA('a')     $foo->setA('a')
Line 294: Line 262:
 The result in all cases is that ''%%a%%'' will be set, while ''%%b%%'' and ''%%c%%'' will not: The result in all cases is that ''%%a%%'' will be set, while ''%%b%%'' and ''%%c%%'' will not:
  
-<code php>+<code>
 object(Foo)#1 (3) { object(Foo)#1 (3) {
   ["a"]=>   ["a"]=>
Line 304: Line 272:
 } }
 </code> </code>
-//I.e. COPA is not an atomic operation in the same way method chaining isn’t.//+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 a tentative list of features that may not be implemented.+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 do that ===
  
-The following examples show some things that is now possible using regular property accessor, but which will not also be supported with COPA:+The following examples show various things that are currently possible when using regular property accessor, though they won’t work inside a COPA block:
  
 <code php> <code php>
Line 330: Line 298:
 ]; ];
 </code> </code>
-//If anyone can show that any these features would be significantly desirable and simultaneously rather trivial to implement, let’s discuss.//+//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 in the same block:+The following syntax could be supported in the future:
  
 <code php> <code php>
 // This example, using current syntax... // This example, using current syntax...
 +
 $foo->a = 1; $foo->a = 1;
 $foo->b->c = 2; $foo->b->c = 2;
  
 // Could be written with COPA like this: // Could be written with COPA like this:
 +
 $foo->[ $foo->[
     a = 1,     a = 1,
Line 350: Line 320:
  
 // 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 360: Line 331:
 ===== 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!** 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.
  
 ===== Proposed PHP Version(s) ===== ===== Proposed PHP Version(s) =====
Line 370: Line 343:
 ==== Alternative syntaxes ==== ==== Alternative syntaxes ====
  
-Some alternative syntaxes for COPA has been suggested, but I’m not convinced they can be implemented without hassle:+I’m going to suggest some alternative syntaxes, which we can vote on, provided their feasibility has been vetted by an experienced internals developer: 
 + 
 +=== Syntax A === 
 + 
 +This is the originally proposed one:
  
 <code php> <code php>
-$foo = new Foo()[ +$foo->[ 
-   property1 "hello"+    a 1, 
-   property2 5+    b = 2, 
- ];+    c = (new Foo)->
 +        3
 +        4, 
 +    ]
 +];
 </code> </code>
-For some reason it’s not possible to do this:+=== Syntax B === 
 + 
 +Since the [[https://wiki.php.net/rfc/deprecate_curly_braces_array_access|deprecation of curly brackets as array access in PHP 7.4]], that notation could be used to assign properties:
  
 <code php> <code php>
-new Foo()->doSomething(); // syntax errorunexpected '->'+$foo{ 
 +    a = 1, 
 +    b = 2, 
 +    c = (new Foo)
 +        a = 3, 
 +        b = 4, 
 +    }, 
 +};
 </code> </code>
-It’s necessary to wrap the instantiation in brackets:+> 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 === 
 + 
 +No wrapper:
  
 <code php> <code php>
-(new Foo)->doSomething()// Ok+$foo-> 
 +    a = 1, 
 +    b = 2, 
 +    c = (new Foo)-> 
 +        a = 3, 
 +        b = 4, 
 +    ;, 
 +;
 </code> </code>
-Which is why I think it will be necessary in my proposal as well.+Nesting becomes awkward - how do we jump out again?
  
-Furthermore, variable or object directly followed by square brackets usually imply array access on it. That syntax would conflict with COPA.+> **Note!** This looks more like chain of normal assignments, but that can be confusion since those normally return the value assigned, not the object itself.
  
-However, there might be an opening for a cool syntax:+=== Syntax D === 
 + 
 +Repeating the arrow for familiarity with regular property assignment:
  
 <code php> <code php>
-(new Foo){+$foo 
 +    ->a = 1, 
 +    ->b = 2, 
 +    ->c = (new Foo) 
 +        ->a = 3, 
 +        ->b = 4, 
 +    ;, 
 +
 +</code> 
 +Same issues as previous. 
 + 
 +=== Syntax E === 
 + 
 +Like the original but with normal brackets instead of square ones: 
 + 
 +<code php> 
 +$foo->(
     a = 1,     a = 1,
     b = 2,     b = 2,
-    c = 3, +    c = (new Foo)->
-}+        a = 3, 
 +        b = 4, 
 +    ), 
 +);
 </code> </code>
-Since the [[https://wiki.php.net/rfc/deprecate_curly_braces_array_access|deprecation of curly brackets as array access in PHP 7.4]], that notation could be used to assign properties.+=== Syntax F ===
  
 +**WITH** syntax
  
-----+<code php> 
 +myObj.with { 
 +     foo = 10 
 +     bar = foo + 20 
 +
 +</code> 
 +If this is the preferred syntax it will require a new RFC.
  
-//Unless someone can convince me that it’s trivial to implement another syntax that looks even better, my stance is that the people who are going to vote no on this because they don’t find the feature useful are not gonna change their mind if the syntax changes, and the people who find this feature useful will prefer rapid adaptation over solving implementation issues.//+===== Rejected Features ===== 
 + 
 +Some suggested features have been rejected due to the fact that COPA aims to be pragmaticwith 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 has been that COPA is of little use without also enforcing mandatory properties to be set.+Some have voiced criticism that COPA is of little use without also enforcing mandatory properties to be set.
  
-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
  
-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, but this idea has not yet caught on.+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, but this idea has not yet caught on. COPA doesn’t have any obvious way of enforcing mandatory properties.
  
-I have a few ideas of how to make progress with that, but itnot part of COPA itself:+> COPA wont 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.
  
-  * Indicate that a parameter is required to be fully initialized when accepting into a function, f.ex: +//For now you must continue to write your own validation code to be carried out at the appropriate “point of no return”.//
- +
-<code php> +
-class Bar { +
-    public function doTheFoo(!Foo $foo) {...} +
-+
-</code> +
-  * Indicate that a property must be initialized before end of constructor: +
- +
-<code php> +
-class Foo { +
-    required public string $name; +
-+
-</code> +
-But 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 (i.e. like a “transaction”).
  
-That does sound cool as welland may seem like the expected behavior for some.+Though that sounds cool, this is an edge case that won’t have any significant impact. If you were planning to resume gracefully with an incomplete object you should probably reconsider your goals in life.
  
-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/benefit of implementing “transaction” and “rollback” behavior is negative.
  
-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 Choices =====+Voting starts 2020-03-31 and ends 2020-04-13.
  
 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 for voting “No”.+<doodle title="Would you like to add support for COPA?" auth="jgivoni" voteType="single" closed="true"> 
 +   * Yes 
 +   No 
 +</doodle> 
 + 
 +\\
  
-The options will be:+<doodle title="If you voted no, what was the main reason?" auth="jgivoni" voteType="single" closed="true"> 
 +   * I voted yes! 
 +   * 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 language evolution 
 +   * This will be a nightmare to implement and maintain 
 +   * I prefer not to say 
 +</doodle>
  
-  - I voted yes! +\\
-  - 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 negatively limit future changes +
-  - This will be a nightmare to implement and maintain +
-  - I prefer not to say+
  
-This will help understand what the obstacles arewhen studying this RFC in the future, should anyone be tempted to have another shot at object literals et. al.+<doodle title="If you did not like the proposed syntaxwhich alternative would you prefer?" auth="jgivoni" voteType="single" closed="true"> 
 +   * A (the proposed one) 
 +   * B 
 +   * C 
 +   * D 
 +   * E 
 +   * F 
 +   * Irrelevant 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
rfc/compact-object-property-assignment.1584585307.txt.gz · Last modified: 2020/03/19 02:35 by jgivoni