[RFC] switch expression
| From: | Ilija Tovilo | Date: | Wed, 25 Mar 2020 12:10:05 +0000 |
| Subject: | [RFC] switch expression | ||
| Groups: | php.internals | ||
| Request: | Send a blank email to internals+get-109289@lists.php.net to get a copy of this message | ||
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!