Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
--
Larry Garfield
larry@garfieldtech.com
Hi folks. Ilija and I would like to present our latest RFC endeavor,
pattern matching:https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had
this one in-progress for 5 years. :-) (Though it was inactive for many of
those years, in fairness.) Pattern matching was intended as the next
follow up to Enums, as it's a stepping stone toward full ADT support.
However, we also feel it has enormous benefit on its own for simplifying
complex comparisons.This RFC has been through numerous iterations, including a full
implementation rewrite just recently that made a number of features much
easier. We have therefore included two patterns that were previously
slated for later inclusion but turned out to be trivially easy in the new
approach. (Variable pinning and numeric comparison.)Nonetheless, there are two outstanding questions on which we are looking
for feedback.Naturally given the timing, we will not be calling a vote until at least
late January, regardless of how the discussion goes. So, plenty of time to
express your support. :-)
Hi Larry,
Looking decent so far.
match() seems a bit clunky in current state. Is it just your code example,
and you could come with another one?
The match(true) and repeating of $somevar over and over is the clunky part.
Normal match($somevar) .. this is clean and works like switch() so people
are happy adopting it .. whereas match() and match() is, look similar but
you have to use them differently ?
Can you clarify?
Many thanks,
Paul
--
Larry Garfield
larry@garfieldtech.com
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
Hi Larry,
Looking decent so far.
match() seems a bit clunky in current state. Is it just your code
example, and you could come with another one?The match(true) and repeating of $somevar over and over is the clunky part.
Normal match($somevar) .. this is clean and works like switch() so
people are happy adopting it .. whereas match() and match() is, look
similar but you have to use them differently ?Can you clarify?
I think you're misinterpreting the example.
The first block under "match() enhancement" is the syntax we propose to have.
The second block is what it would decompose to, aka, an explanation of what it means. While you could write the second block, you really shouldn't when the first block is available and better.
--Larry Garfield
On Mon, Dec 1, 2025, 9:39 PM Larry Garfield larry@garfieldtech.com
wrote:Hi folks. Ilija and I would like to present our latest RFC endeavor,
pattern matching:https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had
this one in-progress for 5 years. :-) (Though it was inactive for many of
those years, in fairness.) Pattern matching was intended as the next
follow up to Enums, as it's a stepping stone toward full ADT support.
However, we also feel it has enormous benefit on its own for simplifying
complex comparisons.This RFC has been through numerous iterations, including a full
implementation rewrite just recently that made a number of features much
easier. We have therefore included two patterns that were previously
slated for later inclusion but turned out to be trivially easy in the new
approach. (Variable pinning and numeric comparison.)Nonetheless, there are two outstanding questions on which we are
looking for feedback.Naturally given the timing, we will not be calling a vote until at
least late January, regardless of how the discussion goes. So, plenty of
time to express your support. :-)Hi Larry,
Looking decent so far.
match() seems a bit clunky in current state. Is it just your code
example, and you could come with another one?The match(true) and repeating of $somevar over and over is the clunky
part.Normal match($somevar) .. this is clean and works like switch() so
people are happy adopting it .. whereas match() and match() is, look
similar but you have to use them differently ?Can you clarify?
I think you're misinterpreting the example.
The first block under "match() enhancement" is the syntax we propose to
have.The second block is what it would decompose to, aka, an explanation of
what it means. While you could write the second block, you really
shouldn't when the first block is available and better.
Hi Larry,
You are correct. Ignore my comment I thought it was showing the opposite.
match() looks good 👍
Many thanks,
Paul
--Larry Garfield
Hey Larry,
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
Thanks for bringing pattern matching up for discussion again.
I'd like to note that the class-access is very ugly.
// Shorthand
if ($p is Point(:$z, x: 3, :$y)) {
print "x is 3 and y is $y and z is $z.";
}
The RFC gives as reasoning that the colon prefix is needed for support
of positional parameters in ADTs. Sure. That's fine to anticipate these.
But what's not fine is using an inconsistent syntax for variable
bindings across different contexts. In arrays binding is just a bare
variable. In objects it suddenly needs a colon? What.
Also, a colon is very prone to being missed in the future with ADTs.
Point::2D($y, $x) vs Point::2D(:$y, :$x). Means something completely
different, but if you mess up just having the colon there or not, is a
serious problem.
Can we instead find some solution, which satisfies both and still
delivers consistency?
An earlier iteration of the RFC had the following very nice construction:
$p is Point&{ $z, x: 3, $y }
This just worked. It's a Point class, and then it matches the properties
of the object. Nice.
This also works for future ADTs. Move::Forward&{ $amount }. Then, if
there's a desire to actually positionally match an object. Then it's
logical to use a parenthesized expression, for a tuple. I.e.:
$move is Move::Forward($a)
Where $a is assigned the first value passed to Move::Forward.
Similarly for destructuring without class name no longer works:
$json = json_decode($myInput);
if ($json is stdClass(type: "store", :$value)) {
// why do I need to know/specify that it's a stdclass?! I'm just
interested in the properties.
}
vs.
if ($json is { type: "store", $value }) {
//
}
This satisfies the requirements of keep the language clear and intuitive:
- Any standalone variable is bound. No weird colon shenanigans. The
syntax is consistent. - Positional binding is quite intuitively using parenthesis - you
construct the enum with Foo::Bar($var) and you read it back on the right
hand side with Foo::Bar($var). - It naturally allows destructuring without class name.
- It makes it hard to accidentally write something totally different to
what was meant.
(Also, it's likely more intuitive to users from other languages, like
rust, which also has {} for named stuff and () for positional stuff.)
Further this particular syntax works nicely with a future scope of
object destructuring, akin to array destructuring. As an example:
function addVec(Point $p, Vec $v) {
Point(:$px, :$py) = $p; // I already know this is a Point, why do I
need to repeat it. It also looks ugly and quite a bit like a left-hand
function call. Like... assigning something to a returned reference?
// Or would you do {$px, $py} for object destructuring? Well that's
now truly inconsistent.
Vec(:$vx, :$vy) = $v;
return new Point($px + $vx, $py + $vy);
}
vs.
function addVec(Point $p, Vec $v) {
{$px, $py} = $p; // Plain and simple. Perfectly straightforward.
{$vx, $vy} = $v;
return new Point($px + $vx, $py + $vy);
}
I've also heard a consideration about "Foo::Bar & { $var }" being
ambiguous with respect to "is Foo::Bar now a const or an ADT class".
This may be resolved in the VM. I don't consider this a major issue, and
is simply something which can be disambiguated at optimizer-time or
run-time, depending on what type of symbol it is.
I'm deeply unsatisfied by the handling of object properties:
"Note that matching against a property's value implies reading that
property's value", "If the property is uninitialized, an error will be
thrown." and "If the property is undefined and none of the above apply,
it will evaluate to null and a Warning will be issued."
This is wildly inconsistent with arrays:
"Of particular note, the pattern matching approach automatically handles
array_key_exists() checking. That means a missing array element will not
trigger a warning, whereas with a traditional if ($foo['bar'] === 'baz')
approach missing values must be accounted for by the developer manually."
Sure, a pattern match will read an objects property. Just like it reads
an arrays entry.
I assume the goal is "let's warn when an object property is typoed". But
it just makes for two tiers. arrays get key_exists(), properties do not
get property_exists(). I welcome surprises.
From my point of view, pattern matching is an "is" operation. Thus it
ought expressing isset-like semantics. I.e. the approach for arrays is
correct, and should be mirrored to objects.
I definitely think the approach of "let's warn about typos" is laudable,
but consistency is important.
It also means that uninitialized properties forcibly throw. It also has
subtle ordering implications on the semantics, given that the
implementation internally short-circuits. E.g. (assuming something like
"class ResponseOrError { string $type; Exception $e; string $response; }"):
if ($obj is ResponseOrError { type: "error", exception: $e }) { throw $e; }
does not throw if $exception is uninitialized. and $type is not error.
But $obj is ResponseOrError { exception: $e, type: "error" } will
certainly throw.
It further means that there needs to be some internal checked and you
cannot simply write:
if ($obj is ResponseOrError { exception: $e }) { throw $e; }
This is bad design and takes a lot of flexibility, just for being typo-safe.
There are better approaches towards typo-safety, e.g. in future (PHP 9)
we could change isset() and all other similar checks (coalesce and this
proposal) to immediately throw when a property is checked for existence,
whose name does not exist on a class which is not marked
#[\AllowDynamicProperties].
We should make use of that instead of shoe-horning this into this proposal.
Open questions:
-
match() "is" placement:
I prefer match() is {} rather than an "is" inside the construct.
Simpler to me, but I think either choice is fine. -
Positional array enforcement:
It's relatively simple to intentionally get positional arrays via
array_values(). I also don't think it's unexpected. That's just how
PHP's arrays work. Enforcing positional arrays however will be quite
surprising if e.g. an entry was removed:
$a = [1, 2, 3];
unset($a[1]);
if ($a is [1, 3]) {
// huh? It's [1, 2 => 3], not [1, 3].
}
Thanks,
Bob
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
--
Larry Garfield
larry@garfieldtech.com
Hi Larry,
Is there a specific reason why less than/etc comparison (and the future scope of ranges) is limited to numerics?
'A' < 'K' comparisons are already in the language, and range() accepts string arguments to produce an array of bytes.
Cheers
Stephen
Hi Larry,
Is there a specific reason why less than/etc comparison (and the future
scope of ranges) is limited to numerics?'A' < 'K' comparisons are already in the language, and
range()accepts
string arguments to produce an array of bytes.Cheers
Stephen
Because neither of us ever use comparisons on strings, as it's not super reliable, so it never came up. :-) We'd prefer to keep it numeric, as the logic otherwise could get confusing. (Personally I'd prefer to even exclude numeric strings, but that ends up being more work.)
--Larry Garfield
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
--
Larry Garfield
larry@garfieldtech.com
Thank you for the RFC and that you included option with match and individual IS’s, I think it would be preferable option not to break the match construction.
Regarding the patterns, is there a way to declare and reuse the patterns? Assign it to a variable and use it as “is $pattern” or something like that. Maybe as a separate class “$pattern->match($var)”.
While it’s not reusable it looks like it has a limited scope to use it, otherwise regular replacements preferred.
--
Best regards,
Dmitrii Derepko.
@xepozz
Regarding the patterns, is there a way to declare and reuse the patterns? Assign it to a variable and use it as “is $pattern” or something like that. Maybe as a separate class “$pattern->match($var)”.
There is an annex document on more speculative extensions linked in the
RFC that mentions this:
https://github.com/Crell/php-rfcs/blob/master/pattern-matching/future.md
I was going to raise the thought of using these patterns to constrain
function parameters (despite the heavy overlap of "$var is type" and
"type $var"), but found that already included in the annex as well.
Thank you for the RFC and that you included option with match and
individual IS’s, I think it would be preferable option not to break the
match construction.Regarding the patterns, is there a way to declare and reuse the
patterns? Assign it to a variable and use it as “is $pattern” or
something like that. Maybe as a separate class “$pattern->match($var)”.
While it’s not reusable it looks like it has a limited scope to use it,
otherwise regular replacements preferred.
Not currently, no. It's something we've discussed, but it's also kinda hard, and I don't believe any other languages do it.
What you could do is just wrap it in a short closure.
$pattern = fn($x) => $x is some|Pattern|null;
$pattern($something);
I'd love to have a way to shorten that, but so far we haven't come up with one. Suggestions welcome, as I'd love to be able to do something like this (obviously not exactly this):
$filtered = array_filter($arr, is Point(x: <10));
--Larry Garfield
$filtered = array_filter($arr, is Point(x: <10));
Btw, it looks incorrect and short closure is preferable here
array_filter($arr, fn($x) => $x is Point())
Or if it works with DFA
array_filter($arr, ? is Point())
If your example illustrated a pattern as an argument I think it’s not valid case, because a pattern is something after “is”? The point, enumerated types or values. And “is” here is an action/function/intention to match left and right operands.
But kotlin style is perfectly matched here
array_filter($arr, { $it is Point() })
--
Best regards,
Dmitrii Derepko.
@xepozz
$filtered = array_filter($arr, is Point(x: <10));
Btw, it looks incorrect and short closure is preferable here
array_filter($arr, fn($x) => $x is Point())
As I said, that's not actually a syntax I'm proposing. Just spitballing that it would be nice to be able to remove the fn($x) => $x boilerplate at some point. But that's not for this RFC; for now, the fact that you can wrap a pattern up into a short closure is sufficient for reuse.
Or if it works with DFA
array_filter($arr, ? is Point())If your example illustrated a pattern as an argument I think it’s not
valid case, because a pattern is something after “is”? The point,
enumerated types or values. And “is” here is an
action/function/intention to match left and right operands.But kotlin style is perfectly matched here
array_filter($arr, { $it is Point() })
Ilija would agree with you, and has been arguing for a { $0 + 3 } type syntax for extra-short closures for a while now. :-) But that's not in scope for the moment.
--Larry Garfield
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
--
Larry Garfield
larry@garfieldtech.com
One thing I didn't see covered: accessing static variables in a class:
class Point {
public static $origin = new Point(0,0,0);
}
How do we pattern match to this? Is it the obvious Point::$origin? Same with class constants?
— Rob
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
https://wiki.php.net/rfc/pattern-matching
You may note the date on the RFC is from 2020. Yes, we really have had this one in-progress for 5 years. :-) (Though it was inactive for many of those years, in fairness.) Pattern matching was intended as the next follow up to Enums, as it's a stepping stone toward full ADT support. However, we also feel it has enormous benefit on its own for simplifying complex comparisons.
This RFC has been through numerous iterations, including a full implementation rewrite just recently that made a number of features much easier. We have therefore included two patterns that were previously slated for later inclusion but turned out to be trivially easy in the new approach. (Variable pinning and numeric comparison.)
Nonetheless, there are two outstanding questions on which we are looking for feedback.
Naturally given the timing, we will not be calling a vote until at least late January, regardless of how the discussion goes. So, plenty of time to express your support. :-)
--
Larry Garfield
larry@garfieldtech.comOne thing I didn't see covered: accessing static variables in a class:
class Point {
public static $origin = new Point(0,0,0);
}How do we pattern match to this? Is it the obvious Point::$origin? Same with class constants?
— Rob
I literally saw it just after sending the email and missed the "undo send" by milliseconds. Sorry for the noise.
— Rob
Hi Larry and Ilija,
Le lun. 1 déc. 2025 à 22:37, Larry Garfield larry@garfieldtech.com a écrit :
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
Thanks for this RFC, it looks really great!
I see that resources are never mentioned. I know that a lot of
resources are being converted to opaque classes in recent versions,
but they still exist. Would it be possible to use the "resource" type
with pattern matching, e.g. $r is resource? I would assume that the
answer is yes because the RFC mentions the support of $foo is iterable.
— Alexandre Daubois
Hi Larry and Ilija,
Le lun. 1 déc. 2025 à 22:37, Larry Garfield larry@garfieldtech.com a écrit :
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
Thanks for this RFC, it looks really great!
I see that resources are never mentioned. I know that a lot of
resources are being converted to opaque classes in recent versions,
but they still exist. Would it be possible to use the "resource" type
with pattern matching, e.g.$r is resource? I would assume that the
answer is yes because the RFC mentions the support of$foo is iterable.
iterable is already a type used elsewhere. resource is not something you can specify anywhere in code right now. Making pattern matching the one place you can actually type check against resource, which is not an inconsistency we want. Also, resources are on their way out, gradually, so we'd just need to remove support from pattern matching again in the future sometime.
In all, it's just not worth the effort and inconsistency.
--Larry Garfield
On Mon, Dec 1, 2025 at 11:39 PM Larry Garfield larry@garfieldtech.com
wrote:
Hi folks. Ilija and I would like to present our latest RFC endeavor,
pattern matching:
Great work, very detailed and well written.
-
There are mentions that any type present in a function parameter
signature and return value is a valid pattern matching.
But are there any plans to extend the pattern-matching-syntax to parameter
types and return values?
Of course, with some restrictions: without variable pinning.And without
variable binding on return type, but that could work on parameter types.
If that's possible or already planned, I think it's worth mentioning it in
the future scope section. -
Maybe some parts of the RFC could be separated to reduce complexity.
Thinking about variable pinning. -
For match() "is" placement, I would go with the second form as it's more
flexible, but I think allowing both forms might be nice if the complexity
is not too high.
Or go only with the second form now, and add the first form later in a
smaller RFC.
--
Alex
Hi folks. Ilija and I would like to present our latest RFC endeavor, pattern matching:
Great work, very detailed and well written.
Thanks!
- There are mentions that any type present in a function parameter
signature and return value is a valid pattern matching.
But are there any plans to extend the pattern-matching-syntax to
parameter types and return values?Of course, with some restrictions: without variable pinning.And without
variable binding on return type, but that could work on parameter types.
If that's possible or already planned, I think it's worth mentioning it
in the future scope section.
See the linked "speculative extensions" document. It's mentioned there, but it hasn't gone beyond "Larry thinks it would be kinda cool to write public int $x is >0. That's definitely out of scope for now, but I'm very open to discussing it in the future.
- Maybe some parts of the RFC could be separated to reduce complexity.
Thinking about variable pinning.
Actually, with the new implementation variable pinning turned out to be stupidly easy. That was a convenient bonus, which is why we included it in the initial RFC. It was originally in future scope, but the diff to add it was like 10 lines or something, so we included it now. The future-scopes that are still in future-scope are all "harder than they look" or "deciding what exactly to do will be a lot of discussion", which is why they're there and not in the base release.
- For match() "is" placement, I would go with the second form as it's
more flexible, but I think allowing both forms might be nice if the
complexity is not too high.
Or go only with the second form now, and add the first form later in a
smaller RFC.
Noted! I think that's one vote for a single is, and one vote for a separate one for each arm. :-)
--Larry Garfield
On Tue, Dec 2, 2025 at 10:09 PM Larry Garfield larry@garfieldtech.com
wrote:
- There are mentions that any type present in a function parameter
signature and return value is a valid pattern matching.
But are there any plans to extend the pattern-matching-syntax to
parameter types and return values?Of course, with some restrictions: without variable pinning.And without
variable binding on return type, but that could work on parameter types.
If that's possible or already planned, I think it's worth mentioning it
in the future scope section.See the linked "speculative extensions" document. It's mentioned there,
but it hasn't gone beyond "Larry thinks it would be kinda cool to write
public int $x is >0. That's definitely out of scope for now, but I'm
very open to discussing it in the future.
That is not exactly what I meant.
Since all types are valid patterns, can't we enlarge the type set to
include more things from the pattern set? in future RFCs.
Something like this: function process(Point(x: 3|4, y: $y) $p): ['a' => int, 'b' => float & >0].
--
Alex
- There are mentions that any type present in a function parameter
signature and return value is a valid pattern matching.
But are there any plans to extend the pattern-matching-syntax to
parameter types and return values?Of course, with some restrictions: without variable pinning.And without
variable binding on return type, but that could work on parameter types.
If that's possible or already planned, I think it's worth mentioning it
in the future scope section.See the linked "speculative extensions" document. It's mentioned there, but it hasn't gone beyond "Larry thinks it would be kinda cool to write
public int $x is >0. That's definitely out of scope for now, but I'm very open to discussing it in the future.That is not exactly what I meant.
Since all types are valid patterns, can't we enlarge the type set to
include more things from the pattern set? in future RFCs.
Something like this:function process(Point(x: 3|4, y: $y) $p): ['a' => int, 'b' => float & >0].--
Alex
Yes, that's also in speculative extensions, under the "Parameter or return guards" section. :-)
I think it would be cool (especially if combined with Rob's type aliases), but that's out of scope for now.
--Larry Garfield
Hi
Am 2025-12-02 21:06, schrieb Larry Garfield:
- Maybe some parts of the RFC could be separated to reduce
complexity.
Thinking about variable pinning.Actually, with the new implementation variable pinning turned out to be
stupidly easy. That was a convenient bonus, which is why we included
it in the initial RFC. It was originally in future scope, but the diff
to add it was like 10 lines or something, so we included it now. The
future-scopes that are still in future-scope are all "harder than they
look" or "deciding what exactly to do will be a lot of discussion",
which is why they're there and not in the base release.
There are different dimensions to complexity. Your reply focuses on just
“implementation complexity”, but there's also “documentation complexity”
or “deciding-how-the-syntax-should-look complexity”. I feel that the
syntax for “variable pinning” specifically has the weakest arguments in
favor of it, with the main argument being “Ruby has it” rather than “it
makes sense”. The other syntax choices either are “obviously correct” or
have good arguments backing the choice.
Best regards
Tim Düsterhus
Hi
Am 2025-12-01 22:36, schrieb Larry Garfield:
Hi folks. Ilija and I would like to present our latest RFC endeavor,
pattern matching:
Thank you. I've already provided some feedback on the PR (and in Ilija's
DMs) after seeing it “show up” in php-src, but I promised to put it
on-list. I'm doing that now. This email is not a full review of the RFC,
just the parts that I already noted. I think I also noted them before in
the previous on-list “pre RFC” discussion.
I'd like to see $foo is $bar with a single top-level variable binding
on the right side disallowed at compile time. This pattern is just an
elaborate way of writing an assignment, thus (almost) never useful, but
possibly confusing to folks coming from Python or just generally
unfamiliar with PHP's pattern matching. I'd be okay with allowing $foo is ($bar) with the explicit parentheses, since this is an established
pattern to indicate “yes, I meant it like this” when using assignments
inside of conditionals, such as while (($row = $statement->fetch()))
as a short form of while (($row = $statement->fetch()) !== false).
I'd like to see required parentheses around “combinator patterns”
(specifically & and |). The RFC currently claims:
While patterns may resemble other language constructs, whatever follows
is is a pattern, not some other instruction.
which is false. “Whatever follows is a pattern, unless it no longer is a
pattern” would be more accurate, which is not very helpful to
intuitively determine the end of a pattern when visually scanning the
code, particularly with the spacing around the combinators that the RFC
suggests.
The main issue is that pattern matching embeds a DSL with its own syntax
inside of PHP, but without having a clear “pattern ends here delimiter”.
I've also looked at other programming languages with pattern matching
and most only support pattern matching at a small number of “special
locations” (e.g. as part of a match() construct or only within a
function signature) and also do not have a concept of “union” or
“intersection” patterns. PHP allows to embed patterns (and the
associated DSL) into arbitrary (complex) expressions, which is not
something that is commonly seen as far as I can tell. C# appears to
support it as well, but the overall syntax for pattern matching is quite
different there, which makes it hard to directly compare it.
The “atomic patterns” as a top-level are fine, since they are either a
single “word” without any spaces or already have clear delimiters (such
as the array pattern).
To give an example: Consider line wrapping within a single pattern (e.g.
because the class names get long or because the pattern matching
expression is deeply indented). The most natural way I can come up with,
without introducing parentheses, is the following:
if (
$foo is Foo(some: "stuff", :$here)
& Bar(with: "more_stuff")
) { }
I find it incredibly non-obvious that & Bar still belongs to the
pattern. And with just a single non-whitespace character change it
becomes something entirely different, but also valid:
if (
$foo is Foo(some: "stuff", :$here)
&& Bar(with: "more_stuff")
) { }
I believe it is not unlikely that the former is interpreted as a typo
for the latter by someone inexperienced with pattern matching.
For $foo is $bar&?User there is some ambiguity if that should have
been ($foo is $bar) ? User : … (i.e. a ternary with a constant User
in the “then” part). It might be clear grammar-wise, but not necessarily
immediately to a reader.
The proposed pattern matching semantics are extremely powerful, but do
not come with any integrated guardrails, which make them extremely
complex and easy to “hold wrong”. I find this a step backwards from the
recent developments making PHP safer and more predictable without
forcing users to learn all rules by heart.
We already had issues with “gobble up everything until you can't” with
short closures and pipes in 8.5. I fear the same happening with pattern
matching. As an example the (future scope) range pattern $foo is 1..=10 can easily be typoed or misremembered as $foo is 1...10 which
to my understanding would be valid PHP code equivalent to ($foo is 1.0) . (0.10) (which is not useful, but valid). Taking into account possible
guardrails right away can help ensure that future additions can be added
in a way that is consistent with existing “look and feel”.
Best regards
Tim Düsterhus