Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:109643 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 77480 invoked from network); 14 Apr 2020 22:18:38 -0000 Received: from unknown (HELO localhost.localdomain) (76.75.200.58) by pb1.pair.com with SMTP; 14 Apr 2020 22:18:38 -0000 To: internals@lists.php.net References: <5e94f84b.1c69fb81.8f1da.317bSMTPIN_ADDED_MISSING@mx.google.com> Date: Tue, 14 Apr 2020 22:48:23 +0200 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:52.0) Gecko/20100101 Firefox/52.0 SeaMonkey/2.49.2 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit X-Posted-By: 46.59.72.204 Subject: Re: [PHP-DEV] Re: [DISCUSSION] Match expression From: ajf@ajf.me (Andrea Faulds) Message-ID: Hi Ilija, Ilija Tovilo wrote: >> I share others' concern that it is inconsistent with PHP's >> existing statements > > Can you elaborate on what exactly the concerns are? Well, I don't think we have any other expressions that are also statements with very slightly different syntax, and in this RFC you propose that the expression and statement forms have different behaviour (expression doesn't support blocks) despite looking the same. So it's neither consistent with other statements and expressions, nor is it internally consistent. >> It is a shame you don't suggest taking this opportunity to add >> pattern matching. > > My main concern here is just complexity. There's so much to consider. > We need to think well about each pattern, how it should work, what it > should look like, how it should be implemented, etc. Furthermore > pattern matching usually isn't restricted to the match expression. > There's often pattern matching for `if`, `while`, `foreach`, function > params, normal assignment, etc. So I don't think it should be mixed > with this RFC. IMO if we implement pattern matching we should make it > available for all of these cases. We already have a limited kind of pattern matching in PHP, destructuring assignment for arrays. You can use it on the left-hand side of any assignment, and also in `foreach`. This pattern can even fail to match, but you get an E_NOTICE instead of a Throwable. It might be nice to have it in some other places, but it isn't necessary. Haskell itself only has it in function definitions, assignments and `case` expressions IIRC, and the former two uses are essentially sugar over use of `case` anyway. Why not take what we have here and support it in `match` too, but with slightly stricter rules so that cases that would otherwise trigger an E_NOTICE lead to choosing a different branch? It would make both our existing pattern-matching and `match` more powerful! Consider interacting with some JSON API: match (json_decode($response)) { ['error' => ['code' => $code, 'msg' => $msg] => { throw new Exception($msg, $code); }, ['result' => $result] => { return $result; }, // No default, so if the JSON doesn't fit this expected format, an error is thrown! :) } This is just a quick example that came to mind, which doesn't really use the full potential available here. Another kind of existing pattern matching is arguably `catch` in exception handling, so maybe we could also have `SomeClass $obj` as a pattern, though that syntax would lead to questions of whether things like `?SomeClass`, `int` and `SomeClass|SomeClass2` should work… >> Using a keyword prefix (e.g. `let`) in future means we end >> up with two kinds of arm (one with === behaviour, one with >> pattern-matching behaviour), but wouldn't it be simpler to >> have just one? > > I personally don't think so. Consider this case: > > ``` > $x = 10; > $y = 20; // This variable is unused > > match ($x) { > $y => { // Always true > echo $y; // Outputs 10 > }, > } > ``` > > `$y` is an identifier pattern and it always matches and assigns the > given value to the variable. This is rather confusing. A keyword like > `let` would make it more obvious that you're using a pattern. I'd also > suspect that it's more common to pass expressions to the lhs of a > match statement than patterns so it makes sense that this is the > default. It would be a bit surprising if you are new to the `match` statement and had not used something similar in another language, indeed. But how often do people use `switch` with cases like this that don't use a constant or literal value? It's not even supported in some popular languages like C, and in the rare case that you need this behaviour, a guard can be used: match ($x) { default if ($x === $y) => { echo $y; } } By the way, having one kinds of arm would mean you don't have to think about the two kinds when reading code like this: match ($x) { $y => { // … }, let $y => { // … }, } > > Also, imagine you want to equate multiple dynamic values with each other: > > ``` > $x = ...; > $y = ...; > $z = ...; > > match ($x) { > $y => ..., > $z => ..., > } > > // If we only have pattern matching > match (true) { > true if $x === $y => ..., > true if $x === $z => ..., > } > ``` True, that is less elegant. But when would you need to do that? Thanks, Andrea