rfc:nullsafe_operator

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:nullsafe_operator [2020/06/06 16:58] – Wording ilijatovilorfc:nullsafe_operator [2020/07/31 08:55] (current) – Close vote ilutov
Line 3: Line 3:
   * Date: 2020-06-02   * Date: 2020-06-02
   * Author: Ilija Tovilo, tovilo.ilija@gmail.com   * Author: Ilija Tovilo, tovilo.ilija@gmail.com
-  * Status: Under discussion+  * Status: Implemented
   * Target Version: PHP 8.0   * Target Version: PHP 8.0
   * Implementation: https://github.com/php/php-src/pull/5619   * Implementation: https://github.com/php/php-src/pull/5619
Line 10: Line 10:
 ===== Introduction ===== ===== Introduction =====
  
-This RFC proposes new operator nullsafe operator ''%%?->%%'' with full short circuiting.+This RFC proposes the new nullsafe operator ''%%?->%%'' with full short circuiting.
  
 ===== Proposal ===== ===== Proposal =====
Line 44: Line 44:
 When the left hand side of the operator evaluates to ''%%null%%'' the execution of the entire chain will stop and evalute to ''%%null%%''. When it is not ''%%null%%'' it will behave exactly like the normal ''%%->%%'' operator. When the left hand side of the operator evaluates to ''%%null%%'' the execution of the entire chain will stop and evalute to ''%%null%%''. When it is not ''%%null%%'' it will behave exactly like the normal ''%%->%%'' operator.
  
-==== Short circuiting ====+===== Short circuiting =====
  
-This RFC proposes full short circuiting. This means when the evaluation of one element in the chain fails the execution of the entire chain is aborted and the entire chain evaluates to ''%%null%%''. The following elements are considered part of the chain.+==== Introduction ==== 
 + 
 +Short circuiting refers to skipping the evaluation of an expression based on some given condition. Two common examples are the operators ''%%&&%%'' and ''%%||%%''. There are three ways the nullsafe operator ''%%?->%%'' could implement short circuiting. We’ll look at the same code snippet for every option. 
 + 
 +<code php> 
 +null?->foo(bar())->baz(); 
 +</code> 
 +**1. Short circuiting for neither method arguments nor chained method calls** 
 + 
 +This complete lack of short circuiting is currently only found in Hack. Both the function ''%%bar()%%'' and the method ''%%baz()%%'' are called. ''%%baz()%%'' will cause a “Call to a member function on null” error. Evaluating method arguments makes it the most surprising of the three options. This was the primary criticism of [[https://wiki.php.net/rfc/nullsafe_calls|the last RFC]]. 
 + 
 +**2. Short circuiting for method arguments but not chained method calls** 
 + 
 +This is what would normally be considered lack of short circuiting. The function ''%%bar()%%'' is not called, the method ''%%baz()%%'' is. ''%%baz()%%'' will cause a “Call to a member function on null” error. 
 + 
 +**3. Short circuiting for both method arguments and chained method calls** 
 + 
 +We’ll refer to this as full short circuiting. Neither the function ''%%bar()%%'' nor the method ''%%baz()%%'' are called. There will be no “Call to a member function on null” error. 
 + 
 +==== Proposal ==== 
 + 
 +This RFC proposes full short circuiting. When the evaluation of one element in the chain fails the execution of the entire chain is aborted and the entire chain evaluates to ''%%null%%''. The following elements are considered part of the chain.
  
   * Array access (''%%[]%%'')   * Array access (''%%[]%%'')
Line 55: Line 76:
   * Nullsafe method call (''%%?->%%'')   * Nullsafe method call (''%%?->%%'')
   * Static method call (''%%::%%'')   * Static method call (''%%::%%'')
-  * Assignment (''%%=%%'', ''%%+=%%'', ''%%??=%%'', ''%%= &%%'', etc.) 
-  * Post/pre increment (''%%++%%'', ''%%--%%'') 
  
 The following elements will cause new sub-chains. The following elements will cause new sub-chains.
  
-  * Right hand side of an assignment 
   * Arguments in a function call   * Arguments in a function call
   * The expression in ''%%[]%%'' of an array access   * The expression in ''%%[]%%'' of an array access
 +  * The expression in ''%%{}%%'' when accessing properties (''%%->{}%%'')
  
 Chains are automatically inferred. Only the closest chain will terminate. The following examples will try to illustrate. Chains are automatically inferred. Only the closest chain will terminate. The following examples will try to illustrate.
Line 81: Line 100:
 //       --------  chain 2 //       --------  chain 2
 // If $c is null chain 2 is aborted, method d() isn't called, null is passed to `$a->b()` // If $c is null chain 2 is aborted, method d() isn't called, null is passed to `$a->b()`
 +</code>
 +==== Rationale ====
  
-   $foo?->bar = $a->b(); +**1. It avoids surprises**
-// -------------------- chain 1 +
-//              ------- chain 2 +
-// If $foo is null chain 1 is aborted, `$a->b()` is not evaluated, the assignment is skipped+
  
-   $foo?->bar++; +<code php> 
-// ------------ chain 1 +$foo = null; 
-// If $foo is null, chain 1 is aborted, ++ is skipped+$foo?->bar(expensive_function());
 </code> </code>
-===== Syntax choice =====+The evaluation of ''%%expensive_function()%%'' is undesirable if ''%%$foo%%'' is ''%%null%%'' as its result will simply be discarded. If the function has side effects it could also lead to surprises.
  
-The syntax has been chosen to indicate the precise place in the code that the short circuiting occurs.+**2. You can see which methods/properties return null** 
 + 
 +<code php> 
 +$foo = null; 
 +$foo?->bar()->baz(); 
 +</code> 
 +Without short circuiting every subsequent method call and property access in the chain will require using the nullsafe operator or you will get a “Call to a member function on null” error. With short circuiting this isn’t necessary which makes it more obvious which methods/properties might return ''%%null%%''
 + 
 +**3. Mixing with other operators** 
 + 
 +<code php> 
 +$foo = null; 
 +$baz = $foo?->bar()['baz']; 
 +var_dump($baz); 
 + 
 +// Without short circuiting: 
 +// Notice: Trying to access array offset on value of type null 
 +// NULL 
 + 
 +// With short circuiting 
 +// NULL 
 +</code> 
 +Since with short circuiting the array access ''%%['baz']%%'' will be completely skipped no notice is emitted.
  
 ===== Other languages ===== ===== Other languages =====
Line 120: Line 160:
 8/13 languages have a nullsafe operator. 4/8 of those implement the nullsafe operator with short circuiting. 8/13 languages have a nullsafe operator. 4/8 of those implement the nullsafe operator with short circuiting.
  
-===== Why short circuiting? =====+===== Syntax choice =====
  
-As with most things short circuiting has benefits and drawbacks.+The ''%%?%%'' in ''%%?->%%'' denotes the precise place in the code where the short circuiting occurs. It closely resembles the syntax of every other language that implements a nullsafe operator.
  
-==== Benefits ====+===== Forbidden usages =====
  
-**1You can see which methods/properties return null**+==== Nullsafe operator in write context ==== 
 + 
 +Using the nullsafe operator in write context ist not allowed.
  
 <code php> <code php>
-// Without short circuiting +$foo?->bar->baz = 'baz'; 
-$foo = null; +// Can't use nullsafe operator in write context
-$foo?->bar()?->baz();+
  
-// With short circuiting +foreach ([1, 2, 3] as $foo?->bar->baz) {} 
-$foo = null+// Can't use nullsafe operator in write context 
-$foo?->bar()->baz();+ 
 +unset($foo?->bar->baz)
 +// Can't use nullsafe operator in write context 
 + 
 +[$foo?->bar->baz] = 'baz'; 
 +// Assignments can only happen to writable values
 </code> </code>
-In this example ''%%$foo%%'' might be ''%%null%%'' but ''%%bar()%%'' will never return ''%%null%%''. Without short circuiting every subsequent method call and property access in the chain will require the nullsafe operator. With short circuiting this isn’t necessary which makes it more obvious which methods/properties might return ''%%null%%''.+It was previously suggested to allow the nullsafe operator in the left hand side of assignments and skip the assignment if the left hand side of the nullsafe operator was ''%%null%%''. However, due to technical difficulties this is not a part of this RFC. It might be addressed in a later RFC. It is also not completely clear whether the right hand side of the assignment should always be evaluated or not.
  
-**2Allows for nullsafe operator in write context**+==== References ==== 
 + 
 +Taking the reference of a nullsafe chain is not allowedThis is because references require l-values (memory locations, like variables or properties) but the nullsafe operator can sometimes return the r-value ''%%null%%''.
  
 <code php> <code php>
-$foo null; +$&$foo?->bar;
-$foo?->bar = 'bar'; +
-var_dump($foo);+
  
-// Without short circuiting: +// Could loosely be translated to
-// Fatal error: Can't use nullsafe result value in write context+
  
-// With short circuiting: +if ($foo !== null) { 
-// NULL+    $x = &$foo->bar; 
 +} else { 
 +    $x = &null; 
 +    // Only variables should be assigned by reference 
 +}
 </code> </code>
-Without short circuiting the assignment to a nullsafe property would be illegal because it produces an r-value (a value that cannot be assigned to)With short circuiting if a nullsafe operation on the left hand side of the assignment fails the assignment is simply skipped. +For this reason, the following examples are disallowed.
- +
-**3. Mixing with other operators**+
  
 <code php> <code php>
-$foo = null; +// 1 
-$baz = $foo?->bar()['baz']+$&$foo?->bar; 
-var_dump($baz);+// Compiler error: Cannot take reference of a nullsafe chain
  
-// Without short circuiting: +// 
-// NoticeTrying to access array offset on value of type null +takes_ref($foo?->bar); 
-// NULL+// ErrorCannot pass parameter 1 by reference
  
-// With short circuiting +// 3 
-// NULL+function &return_by_ref($foo) { 
 +    return $foo?->bar; 
 +    // Compiler error: Cannot take reference of a nullsafe chain 
 +}
 </code> </code>
-Since with short circuiting the array access ''%%['baz']%%'' will be completely skipped no notice is emitted. This might be less of problem once we have a nullsafe array access operator ''%%?[]%%''+Example 2 is a runtime error because we cannot know at compile time if the given parameter allows passing values by reference.
- +
-==== Drawbacks ==== +
- +
-**1. More rules** +
- +
-Short circuiting must define which elements belong to the short circuiting chain and which do not. Not all of them might be immediately obvious but they should be intuitive for the most part. +
- +
-**2. Complexity** +
- +
-It’s also very likely that the implementation of the nullsafe operator with short circuiting will be slightly more complicated than without it. No short circutiing poses it’s own set of complications though (like checking that ''%%?->%%'' can’t be used in write context).+
  
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
Line 188: Line 228:
 Since PHP 7.4 a notice is emitted on array access on ''%%null%%'' (''%%null["foo"]%%''). Thus the operator ''%%?[]%%'' could also be useful (''%%$foo?["foo"]%%''). Unfortunately, this code introduces a parser ambiguity because of the ternary operator and short array syntax (''%%$foo?["foo"]:["bar"]%%''). Because of this complication the ''%%?[]%%'' operator is not part of this RFC. Since PHP 7.4 a notice is emitted on array access on ''%%null%%'' (''%%null["foo"]%%''). Thus the operator ''%%?[]%%'' could also be useful (''%%$foo?["foo"]%%''). Unfortunately, this code introduces a parser ambiguity because of the ternary operator and short array syntax (''%%$foo?["foo"]:["bar"]%%''). Because of this complication the ''%%?[]%%'' operator is not part of this RFC.
  
-The same also goes for a nullsafe function call syntax (''%%$callableOrNull?()%%'').+nullsafe function call syntax (''%%$callableOrNull?()%%''is also outside of scope for this RFC.
  
 ===== Vote ===== ===== Vote =====
  
-+Voting starts 2020-07-17 and ends 2020-07-31.  
 + 
 +<doodle title="Add nullsafe operator to the language?" auth="ilutov" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle>
rfc/nullsafe_operator.1591462702.txt.gz · Last modified: 2020/06/06 16:58 by ilijatovilo