On Thu, 30 Apr 2020 at 13:18, Ilija Tovilo <[email protected]> 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]