Re: [PHP-DEV] Proposal For Return-If / Early Return / Guard ClauseSyntax

This is only part of a thread. view whole thread
  110109
May 10, 2020 16:26 jbafford@zort.net (John Bafford)
Hi Ralph,

> On May 10, 2020, at 11:49, Ralph Schindler <ralph@ralphschindler.com> wrote: > > Hi! > > > # Intro > > I am proposing what is a near completely syntactical addition (only change is to language.y) to the language. The best terminology for this syntax is are: `return if`, "return early", or "guard clauses". > > see: https://en.wikipedia.org/wiki/Guard_(computer_science) > > Over the past few years, I've seen a growing number of blog posts, conference talks, and even tooling (for example code complexity scoring), that suggest writing guard clauses is a good practice to utilize. I've also seen it more prevalent in code, and even attempts at achieving this with Exceptions (in an HTTP context) in a framework like Laravel. > > see abort_if/throw_if: https://laravel.com/docs/7.x/helpers#method-abort-if > > It is also worth mentioning that Ruby has similar features, and I believe they are heavily utilized: > > see: https://github.com/rubocop-hq/ruby-style-guide#no-nested-conditionals > > > # Proposal > > In an effort to make it a first class feature of the language, and to make the control flow / guard clauses more visible when scanning code, I am proposing this in the syntax of adding `return if`. > > The chosen syntax is: > > return if ( if_expr ) [: optional_return_expression] ; > > As a contrived example: > > function divide($dividend, $divisor = null) { > return if ($divisor === null || $divisor === 0); > > return $dividend / $divisor; > } > > There is already a little discussion around the choice of order in the above statement, the main take-aways and (my) perceived benefits are: > > - it keeps the intent nearest the left rail of the code (in normal/common-ish coding standards) > > - it treats "return if" as a meta-keyword; if must follow return for the statement to be a guard clause. This also allows a person to more easily discern "returns" from "return ifs" more easily since there is not an arbitrary amount of code between them (for example if the return expression were after return but before if). > > - it has the quality that optional parts are towards the end > > - is also has the quality that the : return_expression; is very symmetrical to the way we demarcate the return type in method signatures > "): return type {" for example. > > - has the quality of promoting single-line conditional returns > > > # Finally > > One might say this is unnecessary syntactic sugar, which is definitely arguable. But we do have multiple ways of achieving this. > > Of course all of these things should be discussed, I think sub-votes (should this PR make it that far) could be considered. > > The PR is located here: > > https://github.com/php/php-src/pull/5552 > > As mentioned, some discussion is happening there as well. > > > Thanks! > Ralph Schindler > > > PS: since implementing the ::class feature 8 years ago, the addition of the AST abstraction made this kind of syntactical change proof-of-concept so much easier, bravo!
I'm in favor of language features that encourage defensive coding, so, I think the concept behind return-if is good, but your approach too limited. I think a more general guard syntax would be better: guard (some condition) else { //code here must exit the parent block, or else an error is generated (at compile-time if possible) } This would allow for more and broader use cases: the code in the else clause could do any of return, continue, break, throw, exit, or maybe even goto, as appropriate to the condition and its parent block, which could be any functional block — a function, loop, if, else, try, or catch clause, or the "global" scope outside of a function or class definition. And, if you did return (as opposed to something else), you'd retain locality of 'return' and the return value, rather than separating it with the condition. -John
  110111
May 10, 2020 17:32 thomas.lamy@netwake.de (Thomas Lamy)
Am 10.05.20 um 18:26 schrieb John Bafford:
> Hi Ralph, > >> On May 10, 2020, at 11:49, Ralph Schindler <ralph@ralphschindler.com> wrote: >> >> Hi! >> >> >> # Intro >> >> I am proposing what is a near completely syntactical addition (only change is to language.y) to the language. The best terminology for this syntax is are: `return if`, "return early", or "guard clauses". >> >> see: https://en.wikipedia.org/wiki/Guard_(computer_science) >> >> Over the past few years, I've seen a growing number of blog posts, conference talks, and even tooling (for example code complexity scoring), that suggest writing guard clauses is a good practice to utilize. I've also seen it more prevalent in code, and even attempts at achieving this with Exceptions (in an HTTP context) in a framework like Laravel. >> >> see abort_if/throw_if: https://laravel.com/docs/7.x/helpers#method-abort-if >> >> It is also worth mentioning that Ruby has similar features, and I believe they are heavily utilized: >> >> see: https://github.com/rubocop-hq/ruby-style-guide#no-nested-conditionals >> >> >> # Proposal >> >> In an effort to make it a first class feature of the language, and to make the control flow / guard clauses more visible when scanning code, I am proposing this in the syntax of adding `return if`. >> >> The chosen syntax is: >> >> return if ( if_expr ) [: optional_return_expression] ; >> >> As a contrived example: >> >> function divide($dividend, $divisor = null) { >> return if ($divisor === null || $divisor === 0); >> >> return $dividend / $divisor; >> } >> >> There is already a little discussion around the choice of order in the above statement, the main take-aways and (my) perceived benefits are: >> >> - it keeps the intent nearest the left rail of the code (in normal/common-ish coding standards) >> >> - it treats "return if" as a meta-keyword; if must follow return for the statement to be a guard clause. This also allows a person to more easily discern "returns" from "return ifs" more easily since there is not an arbitrary amount of code between them (for example if the return expression were after return but before if). >> >> - it has the quality that optional parts are towards the end >> >> - is also has the quality that the : return_expression; is very symmetrical to the way we demarcate the return type in method signatures >> "): return type {" for example. >> >> - has the quality of promoting single-line conditional returns >> >> >> # Finally >> >> One might say this is unnecessary syntactic sugar, which is definitely arguable. But we do have multiple ways of achieving this. >> >> Of course all of these things should be discussed, I think sub-votes (should this PR make it that far) could be considered. >> >> The PR is located here: >> >> https://github.com/php/php-src/pull/5552 >> >> As mentioned, some discussion is happening there as well. >> >> >> Thanks! >> Ralph Schindler >> >> >> PS: since implementing the ::class feature 8 years ago, the addition of the AST abstraction made this kind of syntactical change proof-of-concept so much easier, bravo! > I'm in favor of language features that encourage defensive coding, so, I think the concept behind return-if is good, but your approach too limited. > > I think a more general guard syntax would be better: > > guard (some condition) else { > //code here must exit the parent block, or else an error is generated (at compile-time if possible) > } > > This would allow for more and broader use cases: the code in the else clause could do any of return, continue, break, throw, exit, or maybe even goto, as appropriate to the condition and its parent block, which could be any functional block — a function, loop, if, else, try, or catch clause, or the "global" scope outside of a function or class definition. And, if you did return (as opposed to something else), you'd retain locality of 'return' and the return value, rather than separating it with the condition. > > -John > Hi all,
In contrast, I really like Ralph's proposal for it's simplicity. I would prefer a single keyword, though.
  110114
May 10, 2020 19:19 benas.molis.iml@gmail.com (Benas IML)
Hello,

I think that we SHOULD not introduce a new keyword (e. g.  guard) since that
would be a "major major" backwards incompatibility. "Guard" is a really
generic
word and a great example of that is Laravel and their authentication guards.

In general, I don't think that early returns require a seperate syntax
and/or
block statement since a simple `if (...) { return; }` is already sufficient
enough.

Best regards,
Benas Seliuginas
  110117
May 10, 2020 20:00 jbafford@zort.net (John Bafford)
Benas,

> On May 10, 2020, at 15:19, Benas IML iml@gmail.com> wrote: > > Hello, > > I think that we SHOULD not introduce a new keyword (e. g. guard) since that > would be a "major major" backwards incompatibility. "Guard" is a really generic > word and a great example of that is Laravel and their authentication guards. > > In general, I don't think that early returns require a seperate syntax and/or > block statement since a simple `if (...) { return; }` is already sufficient > enough. > > Best regards, > Benas Seliuginas
I think there's three main reasons for guard, as opposed to if: 1) It further and clearly establishes intent: if you see guard, you have more information about the programmer's intent and what the code will actually do. 2) It prevents having to negate the condition: guard (is valid) else, instead of if (not valid) then; negations impose additional cognitive load when attempting to understand code, especially if your condition already has a negation. 3) The language provides a guarantee that if the guard condition is not met, execution can not proceed past the else clause. This helps prevent accidental fall-through, such as if you wrote: if(!$valid) { terminate(); } expecting it to end execution (e.g. via a throw or exit), but for some reason it failed to do so. To take an idea from Ralph's original proposal, perhaps some syntax like "if guard (condition) else ...". In this context, guard could not possibly be confused with a function from the parser's point of view, instead serving as an intent modifier to if. -John
  110161
May 15, 2020 08:33 benas.molis.iml@gmail.com (Benas IML)
Hey,

`guard` would be a keyword this means that all of the classes, interfaces
and traits named Guard would be illegal. Therefore Laravel's `Guard`
interface would be incompatible with PHP 8 which in turn means thousands of
web applications would be too.

Best regards,
Benas Seliuginas
  110167
May 15, 2020 13:36 jbafford@zort.net (John Bafford)
Benas,

> On May 15, 2020, at 04:33, Benas IML iml@gmail.com> wrote: > > Hey, > > `guard` would be a keyword this means that all of the classes, interfaces and traits named Guard would be illegal. Therefore Laravel's `Guard` interface would be incompatible with PHP 8 which in turn means thousands of web applications would be too. > > Best regards, > Benas Seliuginas
If the parser were sufficiently smart, I don't think 'guard' would actually conflict with a class or function named 'guard'. If the syntax were: guard (EXPRESSION) else STATEMENT_OR_BLOCK then, 'guard' could not conflict with a class name, because there's no existing syntax that would fit the pattern of a type name followed by a parenthesis — you can't call a type name as if it were a function. Even if you could, it would still not conflict with a function call, because the 'else' keyword after the close parenthesis would not be valid syntax immediately following a function call. So the main question there is, is the parser sufficiently smart to be able to see 'guard' and look ahead for the 'else' to disambiguate a function call from a guard statement? -John
  110168
May 15, 2020 13:57 benas.molis.iml@gmail.com (Benas IML)
Hello,

Not it's not and will likely never be so using `guard` is a really bad idea.

Best regaeds,
Benas Seliuginas