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]