rfc:match_expression_v2

Differences

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

Link to this comparison view

Next revision
Previous revision
rfc:match_expression_v2 [2020/05/18 20:32] – Create RFC ilijatovilorfc:match_expression_v2 [2020/07/09 22:06] (current) – Move to implemented ilutov
Line 1: Line 1:
 ====== PHP RFC: Match expression v2 ====== ====== PHP RFC: Match expression v2 ======
  
-  * Date: 2020-05-16+  * Date: 2020-05-22
   * 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/5371   * Implementation: https://github.com/php/php-src/pull/5371
-  * Supersedes: https://wiki.php.net/rfc/match_expression +  * Supersedes: https://wiki.php.net/rfc/match_expression
  
 ===== Proposal ===== ===== Proposal =====
  
-This RFC proposes adding a new ''match'' expression that is similar to ''switch'' but with safer semantics and the ability to return values.+This RFC proposes adding a new ''%%match%%'' expression that is similar to ''%%switch%%'' but with safer semantics and the ability to return values. 
 + 
 +[[https://github.com/doctrine/orm/blob/72bc09926df1ff71697f4cc2e478cf52f0aa30d8/lib/Doctrine/ORM/Query/Parser.php#L816|From the Doctrine query parser]]:
  
 <code php> <code php>
-$expressionResult = match ($condition) { +// Before 
-    1, 2 => foo(), +switch ($this->lexer->lookahead['type']) { 
-    3=> bar(), +    case Lexer::T_SELECT: 
-    default => baz(),+        $statement = $this->SelectStatement(); 
 +        break; 
 + 
 +    case Lexer::T_UPDATE: 
 +        $statement = $this->UpdateStatement(); 
 +        break; 
 + 
 +    case Lexer::T_DELETE: 
 +        $statement = $this->DeleteStatement(); 
 +        break; 
 + 
 +    default: 
 +        $this->syntaxError('SELECT, UPDATE or DELETE'); 
 +        break; 
 +
 + 
 +// After 
 +$statement = match ($this->lexer->lookahead['type']) { 
 +    Lexer::T_SELECT => $this->SelectStatement(), 
 +    Lexer::T_UPDATE => $this->UpdateStatement(), 
 +    Lexer::T_DELETE => $this->DeleteStatement(), 
 +    default => $this->syntaxError('SELECT, UPDATE or DELETE'),
 }; };
 </code> </code>
- 
 ===== Differences to switch ===== ===== Differences to switch =====
  
 ==== Return value ==== ==== Return value ====
  
-It is very common that the ''switch'' produces some value that is used afterwards.+It is very common that the ''%%switch%%'' produces some value that is used afterwards.
  
 <code php> <code php>
 switch (1) { switch (1) {
     case 0:     case 0:
-        $= 'Foo';+        $result = 'Foo';
         break;         break;
     case 1:     case 1:
-        $= 'Bar';+        $result = 'Bar';
         break;         break;
     case 2:     case 2:
-        $= 'Baz';+        $result = 'Baz';
         break;         break;
 } }
  
-echo $y;+echo $result;
 //> Bar //> Bar
 </code> </code>
- +It is easy to forget assigning ''%%$result%%'' in one of the cases. It is also visually unintuitive to find ''%%$result%%'' declared in a deeper nested scope. ''%%match%%'' is an expression that evaluates to the result of the executed arm. This removes a lot of boilerplate and makes it impossible to forget assigning a value in an arm.
-It is easy to forget assigning ''$y'' in one of the cases. It is also visually unintuitive to find ''$y'' declared in a deeper nested scope. ''match'' is an expression that evaluates to the result of the executed arm. This removes a lot of boilerplate and makes it impossible to forget assigning a value in an arm.+
  
 <code php> <code php>
Line 53: Line 74:
 //> Bar //> Bar
 </code> </code>
- 
 ==== No type coercion ==== ==== No type coercion ====
  
-The ''switch'' statement loosely compares (''=='') the given value to the case values. This can lead to some very surprising results.+The ''%%switch%%'' statement loosely compares (''%%==%%'') the given value to the case values. This can lead to some very surprising results.
  
 <code php> <code php>
 switch ('foo') { switch ('foo') {
     case 0:     case 0:
-      echo "Oh no!\n";+      $result = "Oh no!\n"; 
 +      break; 
 +    case 'foo': 
 +      $result = "This is what I expected\n";
       break;       break;
 } }
 +echo $result;
 +//> Oh no!
 </code> </code>
- +The ''%%match%%'' expression uses strict comparison (''%%===%%'') instead. The comparison is strict regardless of ''%%strict_types%%''.
-The ''match'' expression uses strict comparison (''==='') instead. The comparison is strict regardless of ''strict_types''.+
  
 <code php> <code php>
 echo match ('foo') { echo match ('foo') {
-    0 => "Never reached\n",+    0 => "Oh no!\n", 
 +    'foo' => "This is what I expected\n",
 }; };
 +//> This is what I expected
 </code> </code>
- 
 ==== No fallthrough ==== ==== No fallthrough ====
  
-The ''switch'' fallthrough has been a large source of bugs in many languages. Each ''case'' must explicitly ''break'' out of the ''switch'' statement or the execution will continue into the next ''case'' even if the condition is not met.+The ''%%switch%%'' fallthrough has been a large source of bugs in many languages. Each ''%%case%%'' must explicitly ''%%break%%'' out of the ''%%switch%%'' statement or the execution will continue into the next ''%%case%%'' even if the condition is not met.
  
 <code php> <code php>
Line 88: Line 113:
 } }
 </code> </code>
- +The ''%%match%%'' expression resolves this problem by adding an implicit ''%%break%%'' after every arm.
-This was intended to be a feature so that multiple conditions can execute the same block of code. It is often hard to understand if the missing ''break'' was the authors intention or a mistake.+
  
 <code php> <code php>
-switch ($x) { +match ($pressedKey) { 
-    case 1: +    Key::RETURN_ => save(), 
-    case 2: +    Key::DELETE => delete(), 
-        // Same for 1 and 2 +};
-        break; +
-    case 3: +
-        // Only 3 +
-    case 4+
-        // Same for 3 and 4 +
-        break; +
-}+
 </code> </code>
- +Multiple conditions can be comma-separated to execute the same block of code.
-The ''match'' expression resolves this problem by adding an implicit ''break'' after every arm. Multiple conditions can be comma-separated to execute the same block of code. There's no way to achieve the same result as 3 and 4. This feels natural because the body of a match arm can only contain a single expression.+
  
 <code php> <code php>
Line 113: Line 129:
 }; };
 </code> </code>
- 
 ==== Exhaustiveness ==== ==== Exhaustiveness ====
  
-Another large source of bugs is not handling all the possible cases supplied to the ''switch'' statement.+Another large source of bugs is not handling all the possible cases supplied to the ''%%switch%%'' statement.
  
 <code php> <code php>
-switch ($configuration) { +switch ($operator) { 
-    case Config::FOO+    case BinaryOperator::ADD
-        // ... +        $result = $lhs + $rhs;
-        break; +
-    case Config::BAR: +
-        // ...+
         break;         break;
 } }
 +
 +// Forgot to handle BinaryOperator::SUBTRACT
 </code> </code>
- +This will go unnoticed until the program crashes in a weird way, causes strange behavior or even worse becomes a security hole. ''%%match%%'' throws an ''%%UnhandledMatchError%%'' if the condition isnt met for any of the arms. This allows mistakes to be caught early on.
-This will go unnoticed until the program crashes in a weird way, causes strange behavior or even worse becomes a security hole. Many languages can check if all the cases are handled at compile time or force you to write a ''default'' case if they can't. For a dynamic language like PHP the only alternative is throwing an error. This is exactly what the ''match'' expression does. It throws an ''UnhandledMatchError'' if the condition isn't met for any of the arms.+
  
 <code php> <code php>
-$= match ($x) { +$result = match ($operator) { 
-    => 'Foo', +    BinaryOperator::ADD => $lhs + $rhs,
-    2 => 'Bar',+
 }; };
  
-// Only reached if $is 1 or 2 +// Throws when $operator is BinaryOperator::SUBTRACT
-// $y is 'Foo' or 'Bar'+
 </code> </code>
- 
 ===== Miscellaneous ===== ===== Miscellaneous =====
  
 ==== Arbitrary expressions ==== ==== Arbitrary expressions ====
  
-A match condition can be any arbitrary expression. Analogous to ''switch'' each condition will be checked from top to bottom until the first one matches. If a condition matches the remaining conditions won't be evaluated.+A match condition can be any arbitrary expression. Analogous to ''%%switch%%'' each condition will be checked from top to bottom until the first one matches. If a condition matches the remaining conditions wont be evaluated.
  
 <code php> <code php>
-$= match ($x) {+$result = match ($x) {
     foo() => ...,     foo() => ...,
     $this->bar() => ..., // bar() isn't called if foo() matched with $x     $this->bar() => ..., // bar() isn't called if foo() matched with $x
Line 155: Line 165:
 }; };
 </code> </code>
- 
 ===== Future scope ===== ===== Future scope =====
  
Line 169: Line 178:
  
 <code php> <code php>
-$= match { ... };+$result = match { ... };
 // Equivalent to // Equivalent to
-$= match (true) { ... };+$result = match (true) { ... };
 </code> </code>
- 
 ===== Backward Incompatible Changes ===== ===== Backward Incompatible Changes =====
  
-''match'' was added as a keyword (''reserved_non_modifiers''). This means it can't be used in the following contexts anymore:+''%%match%%'' was added as a keyword (''%%reserved_non_modifiers%%''). This means it cant be used in the following contexts anymore:
  
   * namespaces   * namespaces
Line 184: Line 192:
  
 Note that it will continue to work in method names and class constants. Note that it will continue to work in method names and class constants.
 +
 +===== Syntax comparison =====
 +https://gist.github.com/iluuu1994/11ac292cf7daca8162798d08db219cd5
  
 ===== Vote ===== ===== Vote =====
  
-Voting started 2020-xx-xx and closes 2020-xx-xx.+Voting starts 2020-06-19 and ends 2020-07-03.  
 + 
 +As this is a language change, a 2/3 majority is required.
  
 +<doodle title="Add match expressions to the language?" auth="ilutov" voteType="single" closed="true">
 +   * Yes
 +   * No
 +</doodle>
rfc/match_expression_v2.1589833928.txt.gz · Last modified: 2020/05/18 20:32 by ilijatovilo