Just piping up in a sort of pre-discussion way to see if there might be any interest in making "return" an expression - in the same way and for many (though not all) of the same reasons that "throw" was made an expression in 8.0.

Both return and throw "end the current execution"; the difference is that "throw" does it because things went bad, while "return" does it because things went well and nothing more needs doing.

So, for example:


        $result = query('foo') ?? return false;

rather than

        if(($result = query('foo')) === null) return false;


or

        $split = match($len) {
                0 => throw new UnderflowException("Unexpectedly empty"),
                1 => return false,
                2,3,5,7 => return true,
                4,6,8,9 => $this->smol($len),
                default => $this->foo($len, $scale)
        };


instead of

        if($len === 0)
                throw new UnderflowException("Unexpectedly empty");
        if($len === 1)
                return false;
        if($len === 2 || $len === 3 || $len === 5 || $len === 7)
                return true;
        if($len === 4 || $len === 6 || $len === 8 || $len === 9)
                $split = $this->smol($len);
        else
                $split = $this->foo($len, $scale);

.

A return expression works entirely by its side-effects (passing control and the value of its operand back to the calling scope); its own type is "never".

The weirdest behaviour I can see at this early stage is the main way it deviates from "reasons to have throw as an expression":

        fn($x) => return $x * 2

would work, but not in the way you'd think it does; it expands out to

        function($x) {
                return return $x * 2;
        }

Which (like "throw throw") is syntactically legal, but the redundant operator is redundant. Semantically, however, it could be problematic: The anonymous function's return type is technically "never", since that is what the type of its return statement's operand is. But that return statement never(!) completes, because said operand causes the function to end prematurely, causing the numeric value to be returned instead. And, of course, a numeric value is not a "never".

What this will do to type declarations I don't know. But checking that the body of an arrow function is a return expression and at least notifying the author that it's redundant could be done.

This I guess would also impact ordinary return statements: they are now expressions that evaluate to "never", and that could have consequences for how return types and return type declarations are determined in general...

If necessary, I suppose, the type of a return expression could be that of its operand - it's just that no-one will ever see it because execution is always abandoned before then.

Reply via email to