Hi everybody!
A few years ago I suggested adding a new `match` expression to the PHP language: https://externals.io/message/100487 I arrogantly assumed someone will implement it for me which of course didn't happen. I'd finally like to get my own hands dirty. I have a very rough, incomplete prototype but I'd like to get your feedback before I continue working on the details. # Introduction This is what it looks like: ```php echo $i switch { 0 => "i equals 0", 1 => "i equals 1", 2 => "i equals 2", 3, 4 => "i equals 3 or 4", }; // is roughly equivalent to switch ($i) { case 0: $tmp = "i equals 0"; break; case 1: $tmp = "i equals 1"; break; case 2: $tmp = "i equals 2"; break; case 3: case 4: $tmp = "i equals 3 or 4"; break; default: throw new InvalidArgumentException('Unhandled switch case'); } echo $tmp; ``` Some things to note: * Each case only accepts a single expression * The entire switch expression evaluates to the result of the executed case * There is no fallthrough, an implicit break is added after every case * Multiple case conditions are possible with comma separation * The default case throws a InvalidArgumentException by default * The switch keyword is used as an infix operator # Syntax Originally, I expected to reuse the current syntax and transform it into an expression. ```php $x = switch ($y) { ... }; ``` Turns out this is ambiguous. ```php switch ($y) { ... } [$a] = ...; // Could also be interpreted as switch ($y) { ... }[$a] = ...; ``` I stole the new syntax from C# 8.0 which means at least some people will already be familiar with it: https://docs.microsoft.com/en-us/archive/msdn-magazine/2019/may/csharp-8-0-pattern-matching-in-csharp-8-0#the-evolution-of-pattern-matching-in-c-80 # Type coercion One of the bigger weak points of the `switch` statement is the fact that it performs implicit type coercion. ```php switch ('foo') { case 0: echo "Oh no!\n"; } ``` While it's very tempting to fix this in the new `switch` expression it adds a confusing discrepancy between the `switch` statement and expression. I think it would be preferrable to keep the two the same and change the behavior of both in a new PHP edition (https://github.com/php/php-rfcs/pull/2). # Pattern matching I decided against pattern matching because PHP doesn't have algebraic data types and classes rarely have public properties. In my opinion the limited use cases don't justify the significant complexity added to the language. It would also, once again, add an unjustified discrepancy between the `switch` statement and expression. If at some point we do want to introduce pattern matching it might be better to introduce a different keyword (e.g. `match`) and make it work for both the statement and expression. In case you need to match a more complex expression the following still works fine: ```php echo true switch { is_int($x) => 'int', is_float($x) => 'float', is_string($x) => 'string', ... }; ``` # Blocks Sometimes it would be useful to split the expression into multiple statements to make it more readable. Unfortunately, in PHP there are no block expressions. Rust allows returning the last value by omitting the semicolon: ```php echo $x switch { 1 => { foo(); bar(); baz() }, }; ``` This is indeed possible in PHP and could be implemented as part of the `switch` expression or as a general language feature. A nice side effect is that this could also be used in arrow functions: ```php $x = fn() => { foo(); bar(); baz() }; ``` This would, however, make it inconsistent with closures as they use the return keyword. Thus we would probably have to make sure arrow functions still work with return statement which would decrease the need for such a language construct. It is also very unlike anything else in PHP. # Poll This is a short overview of what I'll be working on in the coming weeks. I created a short poll for you guys to let me know if this idea is worth pursuing: https://forms.gle/stXMv72CAaDDxfwf8 Stay safe!