Hi Nikita

> I think part of the problem is that it's a case of either / or.

I agree that your example is an unpleasant limitation.

> I'm not quite sure what the way forward regarding that is. I can understand
> the reluctance to propose the Rust-style block expression syntax, given its
> currently fairly limited usefulness outside match expressions. I do think
> that this is principally the right approach though, if we want to introduce
> control flow expressions like match.

I personally don't have anything against that approach. I only fear
that proposing a block expression RFC first will most likely fail
because at present the only real use case is arrow functions. If PHP
had block scoping it could have other benefits.

$this->foo = {
    $foo = new Foo();
    $foo->prop1 = ...;
    $foo->prop2 = ...;
    $foo
};

But since $foo escapes the block this isn't incredibly helpful either
other than making it a little more readable. There are also a few
issues we'll have to tackle. In my prototype block expressions had the
following grammar:

'{' inner_statement_list expr '}'

This means the following would be invalid.

$x = fn() => {
    return 'Foo';
    // Parse error, expression expected
};

match ($x) {
    0 => {
        echo 'Foo';
        // Parse error, expression expected
    }
}

Neither of those are great. If we define the grammar like this:

'{' inner_statement_list optional_expr '}'

Well have a conflict with statement lists and if statements.

function foo() {
    {
        // Valid today
    }
}

if ($x) {
    // Is this a block expression or just an if block?
};

So to make that implementation compatible we'd have to allow dropping
the semicolon in this case as well which was highly criticized in this
RFC. We'd also have to check on a case per case basis if the ending
expression in the block expression is allowed/required or not.

Since there's quite a bit of time left until the feature freeze I can
try to think of a few solutions and propose this RFC but I'm afraid if
it fails we'll be right back where we started.

> But if we stay consistent with the remaining proposal, then
> $c should be interpreted as a value to match against here, just like 1 and
> 2. The way to actually capture $c should be
>
> match ($value) {
>     let [1, 2, let $c] => ...
> }

This isn't quite how I imagined it. In this example, 1 and 2 are not
arbitrary expressions but literal patterns.
https://doc.rust-lang.org/reference/patterns.html#literal-patterns

Thus every member of the pattern array itself is a pattern, not an
arbitrary expression which would make the let keyword in the array
pattern unnecessary (and actually invalid). Of course it doesn't
necessarily need to be this way. Your example would work but as you
said prefixing every single pattern would be very tedious.

With the pattern only approach, if you wanted to check if some member
of the array has a dynamic value you'd do something like this:

match ($value) {
    let [1, 2, $c] if $c === $y => ...
}

In other words, the let is more of a prefix of the whole arm, not just
a single pattern.

> Another consideration here is that "let" carries a very strong implication
> of block scoping.

Agreed. I couldn't think of a better keyword but I'm definitely open
to suggestions.

> Finally, the use of "," to specify multiple match values could be a
> composition problem. Rust uses | to specify multiple match values, and also
> allows its use in sub-patterns (this is the "or patterns" feature). This
> allows you to write patterns like

Unfortunately using | is not possible for arbitrary expressions as it
collides with the bitwise or operator. However it can be used in
patterns because they would not allow arbitrary expressions.

match ($x) {
    let [1|2, 3|4] => ...,
}

The inconsistency is a little unfortunate. But I don't think there's
another feasible way.

Ilija

-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to