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:
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.
$x = switch ($y) { ... };
Turns out this is ambiguous.
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:
Type coercion
One of the bigger weak points of the switch
statement is the fact that it performs implicit type coercion.
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:
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:
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:
$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!
Hi Ilija,
śr., 25 mar 2020 o 13:10 Ilija Tovilo ilija.tovilo@me.com napisał(a):
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:
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.$x = switch ($y) { ... };
Turns out this is ambiguous.
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:Type coercion
One of the bigger weak points of the
switch
statement is the fact that
it performs implicit type coercion.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 theswitch
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: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: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:$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!
That looks like what I've described a few months ago in
https://wiki.php.net/rfc/switch-expression-and-statement-improvement
If you dig into the mailing list you can even find almost ready to use
patch which implements it.
I'd love switch expression inclusion in PHP.
Cheers,
Michał Brzuchalski
Hi Michał
I’m sorry, unfortunately I missed your e-mail and RFC.
Let me know if you’re still working on it and I’ll back off of course.
Regards
Hi Michał
I’m sorry, unfortunately I missed your e-mail and RFC.
Let me know if you’re still working on it and I’ll back off of course.
Regards
I like the concept, and it looks like you're both on a similar track. Give or take details, I would very much like to see something like it.
One possible improvement to either version is allowing an expression on the left side. That is, rather than doing an equality match, do a boolean match. That would then allow:
$foo = switch($bar) {
case $bar < 5 => $bar * 3;
case $baz < 10 => $bar * 4;
default => $bar * 5;
};
That would sidestep the need for pattern matching, as you can do anything an expression can do. The obvious caveat of course is figuring out how to reference the variable being switched on, if it's not already a variable. My first thought there is to borrow the $$ variable name from Sara's old function composition proposal, but there may be others. There's likely other issues to discuss here but allowing expressions on the left would greatly improve the expressiveness of the construct.
I'd prefer to not allow multi-line statements on the right, ie, blocks. That leads to too much potential for long and ugly code, which a construct like this should be avoiding. Limiting it to a single expression keeps it compact; if you have more involved logic, then put it in a function and your expression is just... calling that function. Problem solved.
Side note: I did a limited user-space implementation of the same concept a while back:
https://hive.blog/php/@crell/type-matching-in-php
But I'd definitely rather see it in the native syntax.
--Larry Garfield
Thanks for your feedback, Larry!
One possible improvement to either version is allowing an expression on the left side. That is, rather than doing an equality match, do a boolean match.
This is how Rust does it:
let x = match ... {
Some(y) if y < 5 => ...
}
In other words, you can add an additional guard to each case that excepts any expression. We don't really benefit a lot from that since we don't have pattern matching. I don't think this would add any significant benefit over:
$x = true switch {
$x !== null && $x < 5 => ...
}
Regards
Thanks for your feedback, Larry!
One possible improvement to either version is allowing an expression on the left side. That is, rather than doing an equality match, do a boolean match.
This is how Rust does it:
let x = match ... { Some(y) if y < 5 => ... }
In other words, you can add an additional guard to each case that
excepts any expression. We don't really benefit a lot from that since
we don't have pattern matching. I don't think this would add any
significant benefit over:$x = true switch { $x !== null && $x < 5 => ... }
Good point, I'd forgotten about that potential trick. So as long as an expression is allowed on the left, rather than just a literal, which is then == compared against the provided value, that should be "good enough" for most use cases.
The implementation should include some tests to make sure that works properly, but I'm happy with the resulting syntax.
So then the net result is:
$var = switch($val) {
case expr1 => expr2;
}
Where $val gets compared against the result of each expr1, and if true then $var is set to expr2.
Endorse.
--Larry Garfield
Hello together,
Am 25.03.20 um 16:46 schrieb Larry Garfield:
Thanks for your feedback, Larry!
One possible improvement to either version is allowing an expression on the left side. That is, rather than doing an equality match, do a boolean match.
This is how Rust does it:
let x = match ... { Some(y) if y < 5 => ... }
In other words, you can add an additional guard to each case that
excepts any expression. We don't really benefit a lot from that since
we don't have pattern matching. I don't think this would add any
significant benefit over:$x = true switch { $x !== null && $x < 5 => ... }
Good point, I'd forgotten about that potential trick. So as long as an expression is allowed on the left, rather than just a literal, which is then == compared against the provided value, that should be "good enough" for most use cases.
The implementation should include some tests to make sure that works properly, but I'm happy with the resulting syntax.
So then the net result is:
$var = switch($val) {
case expr1 => expr2;
}Where $val gets compared against the result of each expr1, and if true then $var is set to expr2.
on the first glance this all looks nice but you actually created
something more like an if-expression that uses switch as a keyword
because you stripped switch of some of its major features:
- you compare the given value to possible cases -> you compare
expressions to true - you can fall through to other cases without break
- what about the default case?
What about the following if-expression-syntax:
$var = if ($x > 0) { return 1; }
elseif ($x < 0) { return -1; }
else { return 0; }
Maybe this is more in line of what you want to do with your switch
expression?
Greets
Dennis
Hi Dennis
Thanks for your feedback!
you can fall through to other cases without break
You could do the same using the || operator.
what about the default case?
I haven't described the default case in my proposal but it is exactly what you'd expect it to be:
$var = true switch {
$x > 0 => 1,
$x < 0 => -1,
default => 0,
};
What about the following if-expression-syntax:
That would work (once again, Rust already does it) though not with the return keyword. We'd still need a block expression to pass the value from the block to the if expression. When I compare the two I definitely think the match expression is more readable.
Regards
Hello together,
Am 25.03.20 um 16:46 schrieb Larry Garfield:
Thanks for your feedback, Larry!
One possible improvement to either version is allowing an expression on the left side. That is, rather than doing an equality match, do a boolean match.
This is how Rust does it:
let x = match ... { Some(y) if y < 5 => ... }
In other words, you can add an additional guard to each case that
excepts any expression. We don't really benefit a lot from that since
we don't have pattern matching. I don't think this would add any
significant benefit over:$x = true switch { $x !== null && $x < 5 => ... }
Good point, I'd forgotten about that potential trick. So as long as an expression is allowed on the left, rather than just a literal, which is then == compared against the provided value, that should be "good enough" for most use cases.
The implementation should include some tests to make sure that works properly, but I'm happy with the resulting syntax.
So then the net result is:
$var = switch($val) {
case expr1 => expr2;
}Where $val gets compared against the result of each expr1, and if true then $var is set to expr2.
on the first glance this all looks nice but you actually created
something more like an if-expression that uses switch as a keyword
because you stripped switch of some of its major features:
- you compare the given value to possible cases -> you compare
expressions to true- you can fall through to other cases without break
- what about the default case?
What about the following if-expression-syntax:
$var = if ($x > 0) { return 1; }
elseif ($x < 0) { return -1; }
else { return 0; }Maybe this is more in line of what you want to do with your switch
expression?
Or maybe even
$var = $x > 0 ? 1
:($x < 0 ? -1
: 0);
Yes, the required parentheses are ugly, but in my opinion, this is still
better than a new if or switch(true) expression construct.
--
Christoph M. Becker
I don't think this would add any significant benefit over:
$x = true switch { $x !== null && $x < 5 => ... }
The problem with that is that it requires a temporary variable to be
switched on. If I want to switch on, say, a method call, I can write this
for equality:
$result = $this->foo($bar) switch {
1 => 'hello',
2 => 'hi',
3 => 'goodbye'
};
For inequalities, the switch(true) version looks something like this (the
parentheses would probably be optional, but I'd personally use them for
readability):
$temp = $this->foo($bar);
$result = true switch {
($temp <= 1) => 'hello',
($temp == 2) => 'hi',
default => 'goodbye'
}
Using $$ to mean "value tested" would mean you could get rid of the temp
variable, and just write this:
$result = $this->foo($bar) switch {
($$ <= 1) => 'hello',
($$ == 2) => 'hi',
default => 'goodbye'
};
I've also previously thought about specifying an operator for switch
statements, which could be used with expressions as well, e.g.
$result = $this->foo($bar) switch <= {
1 => 'hello',
2 => 'hi',
default => 'goodbye'
};
Again, though, this is something that could be added to switch statements
and expressions together, as a separate RFC.
Regards,
Rowan Tommins
[IMSoP]
Hi Rowan
The problem with that is that it requires a temporary variable to be
switched on. If I want to switch on, say, a method call, I can write this
for equality:
I agree. The iffy part would be recognizing if the case expression should be equated to the switch input or evaluated on its own.
That is still something we could address in a later RFC for both the statement and expression.
Regards
Hi, internals,
---- En mié, 25 mar 2020 17:21:29 +0100 Rowan Tommins rowan.collins@gmail.com escribió ----
I don't think this would add any significant benefit over:
$x = true switch { $x !== null && $x < 5 => ... }
The problem with that is that it requires a temporary variable to be
switched on. If I want to switch on, say, a method call, I can write this
for equality:$result = $this->foo($bar) switch {
1 => 'hello',
2 => 'hi',
3 => 'goodbye'
};
In this case, you can also do:
$result = [
1 => 'hello',
2 => 'hi',
3 => 'goodbye'
][$this->foo($bar)];
With syntax very similar to proposed switch but this is using array.
Regards
Manuel Canga
This doesn't look like it can do fallbacks which are traditional feature of
switch statements, can it? What I mean is this
case foo:
case bar:
return 1;
case baz:
return 2;
If we can't do that, I would suggest to go with different name, rather
than reusing "switch".
Hi Gabriel
This doesn't look like it can do fallbacks which are traditional feature of switch statements, can it?
You can take a look at the tests to get a feel for what it’s like:
https://github.com/php/php-src/pull/5308/files
Multiple conditions are possible:
return $day switch {
1, 7 => false,
2, 3, 4, 5, 6 => true,
};
Regards
Hi!
You can take a look at the tests to get a feel for what it’s like:
https://github.com/php/php-src/pull/5308/files
Multiple conditions are possible:
return $day switch { 1, 7 => false, 2, 3, 4, 5, 6 => true, };
I'm still not sure why if we're calling it "switch" it can't use
traditional switch structure. If it uses something else, it must be
called by some other keyword, it's not a good idea to have two "switch"
constructs in the language with completely different syntax.
--
Stas Malyshev
smalyshev@gmail.com
Hi Stanislav
I'm still not sure why if we're calling it "switch"
C# does it. Not to say everything they do is right but it's reassuring
that a big company like Microsoft had the same approach.
https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/switch
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#switch-expressions
In fact, the approach in the RFC is almost equivalent.
Ilija
Hi!
I'm still not sure why if we're calling it "switch"
C# does it. Not to say everything they do is right but it's reassuring
that a big company like Microsoft had the same approach.
No it isn't. Having two syntaxes for one keyword is not a good idea,
whether Microsoft is doing it or not. I understand why they did it, but
it's still not a good idea.
--
Stas Malyshev
smalyshev@gmail.com
Having two syntaxes for one keyword is not a good idea,
We're already doing that. What about classes vs anonymous objects?
Functions vs closures?
They're using the same keywords. There's no confusion.
Ilija
Having two syntaxes for one keyword is not a good idea,
We're already doing that. What about classes vs anonymous objects?
Functions vs closures?
They're using the same keywords. There's no confusion.Ilija
--
No offence but, that argument is dumb.
A closure and a function are the same thing because the Closure class
comes from a need to have consistent functions regardless of scope.
And an anonymous object still behaves like a normal object, you can
extend it, add methods to it, use traits.
On the contrary having two different meaning for a fundamental construct
is just weird, and if that goes to vote I'll vote against it.
Moreover, no need to repeat mistakes other languages have made.
Best regards
George P. Banyard
Having two syntaxes for one keyword is not a good idea,
We're already doing that. What about classes vs anonymous objects?
Functions vs closures?
They're using the same keywords. There's no confusion.Ilija
There's subtle and important differences there.
An anonymous function is still a function, in that it is a routine that takes input and produces output. A closure is, technically, an anonymous function that has imported variables from its parent scope.
Anonymous classes (not anonymous objects) are implemented as, legitimately, classes; they just have an arbitrary internal name. The object that results can be used exactly like any other object, by design.
I don't believe that's the case here, however. switch
is a language construct for a statement, which branches the flow of control of the program.
What you're proposing is a language construct for an expression, which evaluates depending on internal logic to a different value.
Those are sufficiently distinct that I agree they should have distinct keywords. Plus, the internal syntax is non-trivial to switch back and forth between (break vs not, etc.), so it is misleading for people to present them as two slight variants on the same thing; they're really quite distinct, and that's OK.
My recommendation would be to just borrow Rust's keyword:
$result = match ($var) {
$expression => $expression;
$expression => $expression;
$expression => $expression;
default => $expression;
}
--Larry Garfield
Hi!
My recommendation would be to just borrow Rust's keyword:
$result = match ($var) {
$expression => $expression;
$expression => $expression;
$expression => $expression;
default => $expression;
}
Yes, this would be much better idea.
--
Stas Malyshev
smalyshev@gmail.com
On Sun, 29 Mar 2020 at 23:54, Stanislav Malyshev smalyshev@gmail.com
wrote:
Hi!
My recommendation would be to just borrow Rust's keyword:
$result = match ($var) {
$expression => $expression;
$expression => $expression;
$expression => $expression;
default => $expression;
}Yes, this would be much better idea.
--
Stas Malyshev
smalyshev@gmail.com
Agreed
George P. Banyard
Hi Larry
Thanks for your suggestion.
I chose to use switch instead of match for a couple of reasons:
- It uses the same AST, code generation and opcodes as the switch, I
don't agree that it is significantly different than the switch that we
already have. - Adding the
match
keyword is a breaking change, not an insignificant one. - If we can fix the switch statement semantics in the future the two
will only differ in that one returns a value and the other one
doesn't. This is a much smaller distinction than functions/closure. - If we'd every want to add pattern matching, we'd still have a
keyword available to us.
I don't believe that's the case here, however.
switch
is a language construct for a statement, which branches the flow of control of the program.What you're proposing is a language construct for an expression, which evaluates depending on internal logic to a different value.
Well, I encourage you to look at the implementation. You'll see that
the two are in fact very similar.
I'm not averse to using the match keyword if people are ok with the BC
change. If we do though we definitely should make it work as a
statement, allow blocks in addition to single expression and also fix
type coercion. This would make it a replacement of the switch
statement instead of an addition.
Ilija
Hi!
- It uses the same AST, code generation and opcodes as the switch, I
don't agree that it is significantly different than the switch that we
already have.
This is reasoning in wrong direction. Nobody cares which opcode it uses.
It is significantly different for the user, the fact that it may
compile into same opcode means nothing to the user.
- Adding the
match
keyword is a breaking change, not an insignificant one.
I thought with AST we should be past banning keywords as function etc.
names, aren't we?
- If we can fix the switch statement semantics in the future the two
will only differ in that one returns a value and the other one
doesn't. This is a much smaller distinction than functions/closure.
We don't need to "fix" anything in the existing switch, and likely
couldn't because of immense BC breakage it would cause. If you want
different switch, you better start with different keyword.
- If we'd every want to add pattern matching, we'd still have a
keyword available to us.
What you proposing essentially is pattern matching, just very limited
one. Extending it would be a natural development.
type coercion. This would make it a replacement of the switch
statement instead of an addition.
I don't think we need replacement for switch statement. Adding simple
matching expression seems to be a good idea, but please don't let it get
out of the way and turn into something that is too complex and
controversial and be buried under details that nobody actually needs.
It's better to have sometime simple.
Stas Malyshev
smalyshev@gmail.com
Hi Larry
Thanks for your suggestion.
I chose to use switch instead of match for a couple of reasons:
- It uses the same AST, code generation and opcodes as the switch, I
don't agree that it is significantly different than the switch that we
already have.- Adding the
match
keyword is a breaking change, not an insignificant one.- If we can fix the switch statement semantics in the future the two
will only differ in that one returns a value and the other one
doesn't. This is a much smaller distinction than functions/closure.- If we'd every want to add pattern matching, we'd still have a
keyword available to us.I don't believe that's the case here, however.
switch
is a language construct for a statement, which branches the flow of control of the program.What you're proposing is a language construct for an expression, which evaluates depending on internal logic to a different value.
Well, I encourage you to look at the implementation. You'll see that
the two are in fact very similar.I'm not averse to using the match keyword if people are ok with the BC
change. If we do though we definitely should make it work as a
statement, allow blocks in addition to single expression and also fix
type coercion. This would make it a replacement of the switch
statement instead of an addition.Ilija
Stas already replied and I agree with his statements, so I won't repeat them.
I will, however, add a bit more:
As Stas said, that the internal implementation is nearly the same is irrelevant from a language design perspective; the behavior of the syntax for the user is what matters. Essentially, its "user interface". That should be the driving factor.
Introducing a new keyword in a major release seems entirely within scope. We've added keywords in minors before, too, so I'm not too worried.
As to your other points:
-
Being strictly typed rather than coercively typed: I'd be fine with this, frankly. There may be an argument to be made that it should respect the declare statement, or something else, but making it a new construct puts using === on the table, and I'd be fine with it.
-
Work as a statement; allow blocks.
I disagree with these strongly, for very closely related reasons.
- match/select/whatever it's called is not a statement. It is an expression. It evaluates to something. That's its value. if you just want something that's a control statement, we already have switch. That's it's value.
Not everything has to be a statement. In fact, I've seen arguments before that statements in general are a bad idea and everything should be an expression. There's a reasonably good language design argument to be made there, but at the very least we shouldn't be pretending that statements are intrinsically superior. They're very much not.
The point of statements is to make some change to the state of the system. The point of an expression is to be evaluated into another, simpler value. match() is, specifically, an expression that evaluates to a value. sticking statements inside an expression is just kinda weird, and opens up the door to all kinds of side effect behavior. If you want side effects... you already have switch.
- The use of a single-expression is, I would argue, a feature, not a defect.
As above, expressions evaluate and you usually do not want them to have side effects. Allowing a block on the right side means:
- It encourages side effect code.
- It makes the construct visually larger, not smaller. That makes it harder to read and understand.
- It discourages refactoring of multiple operations into a single operation, viz, a function call.
- You have to have either have a return statement or a magic Rust/Ruby-like "last evaluation gets returned" behavior, which is unprecedented in PHP.
I would argue that in the match() use cases, any time you may be tempted to use a multi-line expression what you really want is a stand-alone function. It creates a cleaner visual in place, plus encourages breaking up code into smaller, named, self-documenting bits. Those could be named functions, anon functions, method calls, etc.
If you have a block of code that ends in a return... it's a function. We have ample ways to define functions already; there's no need to add another one-off way.
Viz, if I see this:
match($foo) {
5 => {
$thing = getThing($foo);
$b = $thing->extract($bar);
return $b * 4;
};
6 => 'blah';
}
Then I would argue this is superior in every situation:
match($foo) {
5 => mapFoo($foo);
6 => 'blah';
}
function mapFoo($foo, $bar) {
$thing = getThing($foo);
$b = $thing->extract($bar);
return $b * 4;
}
As for pattern matching, if it supports an arbitrary left-side expression (or list of expressions) then I'm not clear what else is needed. Isn't this "close enough" to pattern matching that it coves all reasonable use cases:
match(true) {
$foo < 5 => $someVal;
$foo < 10 => $otherVal;
$foo <= 100=> $somethingElse;
default => $whatever.
}
Switching on a type would be a bit more verbose as you'd need to repeat instanceof in each match expression, but I think I'm willing to accept that. Now, if we had proper enumerations and the ability to enforce that you exhausted the possible values (a la Rust), then I'd be on board with internalizing the type match. Sadly, I don't see that happening in the near future, much as I would support it.
--Larry Garfield
Hi Larry
Not everything has to be a statement.
This is not what I meant. What I meant is that it should be usable in
a statement context. Since you mentioned Rust, match is always an
expression. But you can use it without making use of the return value.
This is what I'm suggesting. In PHP this is already for any expression
as well. The only difference is that we require a semicolon.
https://doc.rust-lang.org/reference/expressions/match-expr.html
https://doc.rust-lang.org/reference/statements.html
sticking statements inside an expression is just kinda weird
This is precisely what Rust does.
https://doc.rust-lang.org/reference/expressions/block-expr.html
If you want side effects... you already have switch.
Yes plus all the negatives mentioned in the RFC. It would be
incredibly unfortunate if we created a new construct that fixes all
those problems just to be usable only half the time.
The point of an expression is to be evaluated into another, simpler value.
This is certainly an oversimplification. Function calls are
expressions and still have side effects a lot of the time.
Isn't this "close enough" to pattern matching that it coves all reasonable use cases
I agree. I don't think there's a convincing reason for pattern
matching in PHP right now. Nonetheless, this is not pattern matching
and we shouldn't call it that.
Regards,
Ilija