Re: Re: [DISCUSSION] Match expression

From: Date: Tue, 14 Apr 2020 20:48:23 +0000
Subject: Re: Re: [DISCUSSION] Match expression
References: 1 2 3  Groups: php.internals 
Request: Send a blank email to internals+get-109643@lists.php.net to get a copy of this message
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

« previous php.internals (#109643) next »