rfc:match_expression
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:match_expression [2020/04/19 16:47] – Adjust statement list compilation error message ilijatovilo | rfc:match_expression [2020/04/25 10:32] – Start voting ilijatovilo | ||
---|---|---|---|
Line 2: | Line 2: | ||
* Date: 2020-04-12 | * Date: 2020-04-12 | ||
* Author: Ilija Tovilo, tovilo.ilija@gmail.com | * Author: Ilija Tovilo, tovilo.ilija@gmail.com | ||
- | * Status: | + | * Status: |
* Target Version: PHP 8.0 | * Target Version: PHP 8.0 | ||
* Implementation: | * Implementation: | ||
Line 89: | Line 89: | ||
==== Fallthrough ==== | ==== Fallthrough ==== | ||
- | The '' | + | The '' |
<code php> | <code php> | ||
switch ($pressedKey) { | switch ($pressedKey) { | ||
- | case Key::ENTER: | + | case Key::RETURN_: |
save(); | save(); | ||
// Oops, forgot the break | // Oops, forgot the break | ||
Line 160: | Line 160: | ||
===== Blocks ===== | ===== Blocks ===== | ||
- | Sometimes passing a single expression to a match arm isn't enough, either because you need to use a statement or the code is just too long for a single expression. In those cases you can pass a block to the arm that can contain a list of statements. | + | Sometimes passing a single expression to a match arm isn't enough, either because you need to use a statement or the code is just too long for a single expression. In those cases you can pass a block to the arm. |
<code php> | <code php> | ||
Line 172: | Line 172: | ||
</ | </ | ||
- | It is not possible | + | Originally this RFC included a way to return a value from a block by omitting |
<code php> | <code php> | ||
- | $x = match ($x) { | + | // Original proposal |
- | 0 => { /* How do I return a value? */ }, | + | $y = match ($x) { |
+ | 0 => { | ||
+ | foo(); | ||
+ | bar(); | ||
+ | baz() // This value is returned | ||
+ | | ||
}; | }; | ||
- | //> Fatal error: Match expressions that are not statements can't contain statement lists | ||
- | // The same goes for | + | // Alternative syntax, <= |
+ | $y = match ($x) { | ||
+ | 0 => { | ||
+ | foo(); | ||
+ | bar(); | ||
+ | <= baz(); | ||
+ | }, | ||
+ | }; | ||
+ | |||
+ | // Alternative syntax, separate keyword | ||
+ | $y = match ($x) { | ||
+ | 0 => { | ||
+ | foo(); | ||
+ | bar(); | ||
+ | pass baz(); | ||
+ | }, | ||
+ | }; | ||
+ | |||
+ | // Alternative syntax, automatically return last expression regardless of semicolon | ||
+ | $y = match ($x) { | ||
+ | 0 => { | ||
+ | foo(); | ||
+ | bar(); | ||
+ | baz(); | ||
+ | }, | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | For the time being using blocks in match expressions that use the return value in any way results in a compilation error: | ||
+ | |||
+ | <code php> | ||
+ | $y = match ($x) { | ||
+ | 0 => {}, | ||
+ | }; | ||
+ | //> Match that is not used as a statement can't contain blocks | ||
foo(match ($x) { | foo(match ($x) { | ||
- | 0 => { ... }, | + | 0 => {}, |
}); | }); | ||
+ | //> Match that is not used as a statement can't contain blocks | ||
- | echo 1 + match ($x) { | + | 1 + match ($x) { |
- | 0 => { ... }, | + | 0 => {}, |
}; | }; | ||
+ | //> Match that is not used as a statement can't contain blocks | ||
- | // etc. | + | //etc. |
+ | |||
+ | // Only allowed form | ||
+ | match ($x) { | ||
+ | 0 => {}, | ||
+ | } | ||
</ | </ | ||
- | ===== Semicolon | + | ===== Optional semicolon for match in statement form ===== |
When using '' | When using '' | ||
Line 208: | Line 253: | ||
</ | </ | ||
- | However, to make the '' | + | However, to make the '' |
<code php> | <code php> | ||
Line 216: | Line 261: | ||
</ | </ | ||
- | This introduces | + | This introduces |
<code php> | <code php> | ||
Line 232: | Line 277: | ||
</ | </ | ||
- | When '' | + | A '' |
- | ===== break/ | + | <code php> |
+ | // These work fine | ||
+ | $x = match ($y) { ... } - 1; | ||
+ | foo(match ($y) { ... } - 1); | ||
+ | $x[] = fn($y) => match ($y) { ... }; | ||
+ | // etc. | ||
+ | </ | ||
+ | |||
+ | This is also how Rust solves this ambiguity((https:// | ||
+ | |||
+ | <code rust> | ||
+ | match true { _ => () } - 1; | ||
+ | |||
+ | // warning: unused unary operation that must be used | ||
+ | // --> src/ | ||
+ | // | | ||
+ | // 2 | match true { _ => () } - 1; | ||
+ | // | ||
+ | // | | ||
+ | </ | ||
+ | |||
+ | Because there was some controversy around this feature it was moved to a secondary vote. | ||
+ | |||
+ | ===== Allow dropping (true) condition ===== | ||
+ | It has been suggested to make no condition equivalent to '' | ||
+ | |||
+ | <code php> | ||
+ | match { | ||
+ | $age >= 30 => {}, | ||
+ | $age >= 20 => {}, | ||
+ | $age >= 10 => {}, | ||
+ | default => {}, | ||
+ | } | ||
+ | |||
+ | // Equivalent to | ||
+ | |||
+ | match (true) { | ||
+ | $age >= 30 => {}, | ||
+ | $age >= 20 => {}, | ||
+ | $age >= 10 => {}, | ||
+ | default => {}, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | The keyword '' | ||
+ | |||
+ | <code php> | ||
+ | match { | ||
+ | preg_match(...) => {}, // preg_match returns 1 which is NOT identical (===) to true | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Because I have no strong opinion on this it will be moved to a secondary vote. | ||
+ | |||
+ | ===== Miscellaneous ===== | ||
+ | ==== Arbitrary expressions ==== | ||
+ | A match condition can be any arbitrary expression. Analogous to '' | ||
+ | |||
+ | <code php> | ||
+ | match ($x) { | ||
+ | foo() => ..., | ||
+ | $this-> | ||
+ | $this-> | ||
+ | // etc. | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== break/ | ||
Just like with the switch you can use '' | Just like with the switch you can use '' | ||
Line 260: | Line 372: | ||
</ | </ | ||
- | ===== return | + | ==== goto ==== |
+ | Like with the '' | ||
+ | |||
+ | <code php> | ||
+ | match ($a) { | ||
+ | 1 => { | ||
+ | match ($b) { | ||
+ | 2 => { | ||
+ | goto end_of_match; | ||
+ | }, | ||
+ | } | ||
+ | }, | ||
+ | } | ||
+ | |||
+ | end_of_match: | ||
+ | </ | ||
+ | |||
+ | It is not allowed to jump into match expressions. | ||
+ | |||
+ | <code php> | ||
+ | goto match_arm; | ||
+ | |||
+ | match ($b) { | ||
+ | 1 => { | ||
+ | match_arm: | ||
+ | }, | ||
+ | } | ||
+ | |||
+ | //> Fatal error: ' | ||
+ | </ | ||
+ | |||
+ | ==== return | ||
'' | '' | ||
Line 275: | Line 418: | ||
</ | </ | ||
- | ===== Pattern matching | + | ===== Future scope ===== |
- | I have experimented with pattern matching ((https:// | + | ==== Block expressions ==== |
+ | As mentioned above block expressions will be discussed in a separate RFC. We'll also use this opportunity to think about blocks in arrow functions. | ||
+ | |||
+ | ==== Pattern matching ==== | ||
+ | I have experimented with pattern matching ((https:// | ||
<code php> | <code php> | ||
Line 282: | Line 429: | ||
match ($value) { | match ($value) { | ||
let $a => ..., // Identifer pattern | let $a => ..., // Identifer pattern | ||
+ | let ' | ||
let 0..<10 => ..., // Range pattern | let 0..<10 => ..., // Range pattern | ||
let is string => ..., // Type pattern | let is string => ..., // Type pattern | ||
Line 287: | Line 435: | ||
let Foo { foo: 1, getBar(): 2 } => ..., // Object pattern | let Foo { foo: 1, getBar(): 2 } => ..., // Object pattern | ||
let $str @ is string if $str !== '' | let $str @ is string if $str !== '' | ||
+ | |||
+ | // Algebraic data types if we ever get them | ||
+ | let Ast:: | ||
} | } | ||
// Without pattern matching | // Without pattern matching | ||
match (true) { | match (true) { | ||
- | true => $value ..., // Identifer | + | true => $value ..., // Identifier pattern |
+ | ' | ||
$value >= 0 && $value < 10 => ..., // Range pattern | $value >= 0 && $value < 10 => ..., // Range pattern | ||
is_string($value) => ..., // Type pattern | is_string($value) => ..., // Type pattern | ||
Line 305: | Line 457: | ||
</ | </ | ||
- | While some patterns are significantly shorter (namely | + | ==== Explicit fallthrough ==== |
+ | Some people have suggested allowing explicit fallthrough to the next arm. This is, however, not a part of this RFC. | ||
+ | |||
+ | <code php> | ||
+ | match ($x) { | ||
+ | 1 => { | ||
+ | foo(); | ||
+ | fallthrough; | ||
+ | }, | ||
+ | 2 => { | ||
+ | bar(); | ||
+ | }, | ||
+ | } | ||
+ | |||
+ | // 1 calls foo() and bar() | ||
+ | // 2 only calls bar() | ||
+ | </ | ||
+ | |||
+ | This would require a few sanity checks with pattern matching. | ||
+ | |||
+ | <code php> | ||
+ | match ($x) { | ||
+ | $a => { fallthrough; | ||
+ | $b => { /* $b is undefined */ }, | ||
+ | } | ||
+ | </ | ||
===== "Why don't you just use x" ===== | ===== "Why don't you just use x" ===== | ||
Line 329: | Line 506: | ||
</ | </ | ||
- | This code will execute every single " | + | This code will execute every single " |
==== Nested ternary operators ==== | ==== Nested ternary operators ==== | ||
Line 350: | Line 527: | ||
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. | ||
- | |||
- | ===== Proposed PHP Version(s) ===== | ||
- | The proposed version is PHP 8. | ||
===== Proposed Voting Choices ===== | ===== Proposed Voting Choices ===== | ||
- | As this is a language change, a 2/3 majority is required. | + | As this is a language change, a 2/3 majority is required. |
+ | |||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
+ | Secondary | ||
+ | |||
+ | <doodle title=" | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | |||
+ | Secondary | ||
+ | |||
+ | <doodle title=" | ||
+ | * Yes | ||
+ | * No | ||
+ | </ | ||
+ | |||
+ | ==== If you voted no, why? ==== | ||
+ | |||
+ | - Not interested | ||
+ | - I don't want blocks | ||
+ | - Missing return values in blocks | ||
+ | - Missing pattern matching | ||
+ | - Missing explicit fallthrough | ||
+ | - BC break is not acceptable | ||
+ | - Wanted [[https:// | ||
+ | - Other | ||
+ | |||
+ | <doodle title=" | ||
+ | * 1 | ||
+ | * 2 | ||
+ | * 3 | ||
+ | * 4 | ||
+ | * 5 | ||
+ | * 6 | ||
+ | * 7 | ||
+ | * 8 | ||
+ | </ | ||
rfc/match_expression.txt · Last modified: 2020/05/09 15:59 by ilijatovilo