On Thu, 30 Apr 2020 at 13:18, Ilija Tovilo <tovilo.il...@gmail.com> wrote:

> There are three potential use cases for language wide block expressions.
>
> 1. Match expressions
> 2. Arrow functions
> 3. Everything else
>
> The problem is that they all have slightly different semantics.
> [...]
>


I don't think that's actually true. If I'm understanding you right, you're
concerned about two things:

* Blocks which don't have a return value where one is expected / required.
* Blocks which do have a return value where one is not expected.

The language already has an established convention for both cases: a
function with no return statement evaluates to NULL in expression context,
and a function with a return value can be used in statement context and the
result discarded. I see no immediate reason block expressions couldn't use
the same rule.




> $y = match ($x) {
>     1 => {}, // Error, this does require a return value
> };
>


This could evaluate the block to null, and thus be equivalent to:

$y = match ($x) {
     1 => null,
};



$x = fn() => {}; // This is fine, the function returns null
> $x = fn(): ?int => {}; // Uncaught TypeError: Return value of
> {closure}() must be of the type int or null, none returned
>


I had no idea that was an error; I guess it's the counterpart to ": void" -
a style check rather than an actual return type check. But I don't see a
particular problem with a short closure giving the same error as the
equivalent named function (function foo(): ?int {}) so there doesn't seem
to be anything extra to define here.




> $x = fn() => {
>     foo();
>     bar();
>     <= baz(); // Why should we allow this? You can just use return
> };
>


Because right now, you *can't* use return; there are no block bodied short
closures. If we did allow "return" here, there's no *fundamental* reason
not to also allow it in a match expression, meaning "return this as the
value of the match expression" (we might not *want* to reuse the keyword,
but we *could*).




> // All of these are errors, return value is required
> $x = {};
> foo({});
> {} + 1;
> // etc.
>


They would be evaluated as empty statements, and "return" null:
$x = null;
foo(null);
null + 1;




> It's also highly questionable whether use case 3 is actually very
> useful at all because PHP doesn't have block scoping and all the inner
> variables will leak out into the outer scope.

[...]

> An additional complication is that blocks already exist as "statement
> list" statements
>


We could potentially solve both of these by introducing a new syntax which
made something explicitly a block expression. I'm not sure what the keyword
would be; "do" is already used, and "eval" has bad connotations, so I'll
use "block" as a straw man to demonstrate.

// block expression as RHS of assignment
$this->foo = block {
    $bar = new Bar();
    $foo = new Foo();
    $foo->bar = $bar;
    return $foo;
};
// $this->foo has been assigned, $bar and $foo are no longer in scope


// block expression as arm of match expression
$y = match ($x) {
    1 => block {
        foo();
        return bar();
    },
}
// if $x===1, foo() is executed, then $y gets the result of bar()


// block expression as result of short closure
$f = fn($x) => block { foo($x); bar($x); };
$f();


// even if the expression result isn't used, the scoping could apply
if ( foo() ) block {
    $x = 1;
};
// $x is not defined here
// note trailing semi-colon, for the same reason you need one after a
standard anonymous function definition
// the above is actually equivalent to this:
if ( foo() ) {
    block {
        $x = 1;
    };
}


I don't know if I *like* this idea, but it would be a consistent
language-wide implementation of the concept with minimal compatibility
impact.




It's not that black and white. I work in a lot of legacy projects that
> could benefit from match expressions but it's simply not realistic to
> refactor every single switch statement that contains more than a one
> liner.
>


To use Larry's codenames, would those specifically benefit from "rustmatch"
(evaluating the switch to an expression) or from "switchng" (a stricter
switch statement)? I'd be interested to see a real-life example where you'd
want both the match to evaluate to a value, and the arms to contain more
than one statement.

Regards,
-- 
Rowan Tommins
[IMSoP]

Reply via email to