[RFC] Guard statement

  110174
May 16, 2020 09:13 algerd.blr@gmail.com (Pavel Patapau)
Hello everyone,

I want to propose new syntax addition - guard statement, that executes 
code only if expression equals false and must contain control-flow 
changing code, and written a respective RFC:

https://wiki.php.net/rfc/guard_statement
<https://wiki.php.net/rfc/unbundle_xmlprc>

Implementation in progress.

I started work before this proposal https://externals.io/message/110107 
and respected some moments in the RFC.


Thanks for consideration,
Pavel
  110175
May 16, 2020 09:25 benas.molis.iml@gmail.com (Benas IML)
Hello,

first of all, thank you for the RFC but this is a big no-no for me. Your
arguments as to why it's "okay" to make a BC break doesn't make a lot of sense
given the huge "genericness" of the keyword "guard". Laravel's authentication
system is used across thousands of projects (if not more) and you can look that
up using a simple GitHub search. In fact, even in our internal codebase (our
team's, not PHP's) we use it. So, it's plausible that many closed-source
projects use it as well.

Best regards,
Benas Seliuginas
  110176
May 16, 2020 12:14 maxsem.wiki@gmail.com (Max Semenik)
On Sat, May 16, 2020 at 12:14 PM Pavel Patapau blr@gmail.com> wrote:

> Hello everyone, > > I want to propose new syntax addition - guard statement, that executes > code only if expression equals false and must contain control-flow > changing code, and written a respective RFC
Both your initial email and your RFC mention https://en.wikipedia.org/wiki/Guard_(computer_science) Note how no language mentioned in that article has this keyword, `guard`, or any special language construct, honestly. That's for a reason: the guard statement described in the article is a pattern, not a language construct. Also, consider that one can pronounce naturally "if foo [then] return bar" but "guard foo else return bar" doesn't make sense English. I would go further claim that in my opinion all current control structures in PHP are intuitively understandable to anyone familiar with programming but not the language. Can the same be said about `guard`? -- Best regards, Max Semenik
  110177
May 16, 2020 12:39 Danack@basereality.com (Dan Ackroyd)
On Sat, 16 May 2020 at 13:14, Max Semenik wiki@gmail.com> wrote:
> > Note how no language mentioned in that article has this keyword,
Except Swift which is one of the first languages to use this: https://www.hackingwithswift.com/new-syntax-swift-2-guard
> I would go further claim that in my opinion all current > control structures in PHP are intuitively understandable > to anyone familiar with programming but not the > language.
So you're saying that PHP should only have features that are familiar to C style programming languages, and that we shouldn't either have new features of our own design, or features that are new in Swift?
> "Can the same be said about `guard`?"
Yes. It's something that's new, so you're not used to it. And so there's an instinctive feeling of doubt about it. But it's the type of thing that once introduced would be easy to explain to a junior programmer. To be clear, I have the same feelings of "Oh wow this is totally weird and I don't like it", but the more I think about "If I saw this in some code, would it make reasoning about that code be easier?" the more I get used to it. cheers Dan Ack
  110178
May 16, 2020 12:52 tovilo.ilija@gmail.com (Ilija Tovilo)
Hi Pavel


First of all, thanks for your proposal!

I'm a bit skeptical. guard is mostly useful in Swift because it's a
strictly typed language.

let x = foo() // type Int?
if x == nil { return }
// x is still Int?

Guard is kind of an inverted if statement for them.

guard let x = foo() { return }
// x is Int

In PHP we don't have this problem as there are no (or few) compile
time type checks.

guard (null !== $x = foo()) { return; }
// No benefit over
if (null !== $x = foo()) { return; }

The only benefit is that the block is forced to be terminating but
that's not worth it IMO.

Ilija
  110179
May 16, 2020 12:57 tovilo.ilija@gmail.com (Ilija Tovilo)
Correction:

> guard (null !== $x = foo()) { return; } > // No benefit over > if (null !== $x = foo()) { return; }
My example was wrong, should've been:
> if (null === $x = foo()) { return; }
  110180
May 16, 2020 13:10 george.banyard@gmail.com ("G. P. B.")
On Sat, 16 May 2020 at 11:14, Pavel Patapau blr@gmail.com> wrote:

> Hello everyone, > > I want to propose new syntax addition - guard statement, that executes > code only if expression equals false and must contain control-flow > changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement > <https://wiki.php.net/rfc/unbundle_xmlprc> > > Implementation in progress. > > I started work before this proposal https://externals.io/message/110107 > and respected some moments in the RFC. > > > Thanks for consideration, > Pavel >
This is a fat no from my side. I see no benefit instead of using a normal if construct. This guard syntax has more keystrokes, doesn't read like "normal" English, has massive BC implications. I don't see why this would encourage defensive programming as you still need to write code with this in mind. Moreover, we already have assertions [1] although they could be improved, I'd rather have this be the focus than introducing this guard proposal. Best regards George P. Banyard [1] https://www.php.net/manual/en/function.assert.php
  110181
May 16, 2020 13:56 jbafford@zort.net (John Bafford)
Pavel,

On May 16, 2020, at 05:13, Pavel Patapau blr@gmail.com> wrote:
> > Hello everyone, > > I want to propose new syntax addition - guard statement, that executes code only if expression equals false and must contain control-flow changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement > <https://wiki.php.net/rfc/unbundle_xmlprc> > > Implementation in progress. > > I started work before this proposal https://externals.io/message/110107 and respected some moments in the RFC. > > > Thanks for consideration, > Pavel
Regarding this part of the proposal:
> Body of statement must contain code that changes control flow: return, throw, goto. Also, in a loop context, you can use break or continue. > > If compiler doesn't find any of those statements, it will search selection type statements(if with else|try/catch/finally|switch) and ensures that every element of these statements contains return, throw, goto.
I feel that from an implementation standpoint, this could be fairly complex, especially since code such as: guard (COND) else exit; guard (COND) else call_some_function_that_calls_exit(); ought to compile without warnings. (Exit was explicitly included in my original proposal on the mailing list.) Some languages allow this by marking functions with a noreturn attribute (C, Swift < 3). Swift >= 3 solves this problem by having a "Never" return type, which is validated by Swift's type checker, requiring all code paths to also call another function returning Never. (As an implementation detail, Never is an enum with no cases.) Otherwise, you would have to write guard (COND) else { exit; return; } to satisfy the parser, which is silly. I would suggest an alternative that is conceptually simpler and allows for this use case. We can observe that if we slightly loosen the restriction that the _compiler_ must be able to do a full call path analysis, a guard statement can also be thought of as: guard (COND) else { STATEMENTS; throw new \GuardFailureError; } That is, the else clause ends with a compiler-generated fatal error. This means the compiler itself doesn't _have_ to do a full analysis of every call path in the STATEMENTS to ensure proper exit; the runtime can trap as well. This allows us to be a bit more flexible: if the compiler can prove one way or the other, it can either fail compilation, or else omit generating the opcodes for throwing the GuardFailureError. But code that the compiler _can't_ prove (such as the previously mentioned code-that-calls-exit) should still be allowed. If we later add language support for a noreturn attribute, or some other mechanism to account for that, then we are in a position to allow for that complexity in the parser and remove the runtime handling. It would obviously be preferable if the compiler can catch errors in all cases, but when it can't (and php's dynamicity makes this a hard problem), I think it's acceptable that the runtime can serve as a fallback. -John
  110182
May 16, 2020 14:15 jakob@givoni.dk (Jakob Givoni)
On Sat, May 16, 2020 at 11:14 AM Pavel Patapau blr@gmail.com> wrote:
> > Hello everyone, > > I want to propose new syntax addition - guard statement, that executes > code only if expression equals false and must contain control-flow > changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement > <https://wiki.php.net/rfc/unbundle_xmlprc> > > Implementation in progress. > > I started work before this proposal https://externals.io/message/110107 > and respected some moments in the RFC. > > > Thanks for consideration, > Pavel >
Hi Pavel, My feeling about this is that simply adding the "guard" keyword to be able to write an "negated if" and further requiring certain stuff to be found in the body statement (which does not sounds trivial) is not worth the added weight. I found the inverse "if" construct (return/break/continue/exit/throw [...] if [...]) that was discussed in the thread as well to be more interesting, potentially adding something new and flavourful to the language. However, since I am very much opposed to methods with multiple return statements, I would probably only use it in the throw version for defensive programming. Best, Jakob
  110186
May 16, 2020 15:05 rowan.collins@gmail.com (Rowan Tommins)
On 16/05/2020 10:13, Pavel Patapau wrote:
> I want to propose new syntax addition - guard statement, that executes > code only if expression equals false and must contain control-flow > changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement
Hi Pavel, This certainly seems to be a "problem space" which several people want to explore, but I'm not quite convinced by any of the proposals so far. I read through the links in your RFC, and a few articles about Swift's "guard" syntax, and have a few observations: * An important defining feature of a guard clause is that it occurs *before* any of the logic of the function. Some languages have syntax to codify that, putting guards or pre-conditions as part of the function's header, rather than in its body. * Another defining feature is that guard clauses should be simple, getting easy cases out of the way early. Having "guard ... else" take a whole block of statements, including other flow control, makes it feel less like a guard clause, and more like normal flow control. * Swift's "guard" clause is used for assertions which affect the static analysis of the code. Most notably, "guard let x else { return; }" can be used to check an Optional value, and the compiler will allow that value to be used in the rest of the function without further "unwrapping". That explains the requirement that the guard clause aborts the flow of the function (including calling a function with a "Never" return type, as John Bafford points out). There's not really any equivalent of that in PHP, so including similar limitations feels rather arbitrary. * As with Ralph Schindler's e-mail, Perl and Ruby are being slightly misrepresented here: they don't have explicit syntax for guard clauses per se, they just have more ways of spelling "if", which is useful for making guard clauses read nicely; that includes "unless", which just means "if not". Regarding the specific syntax, I agree with others that "guard something else return" doesn't read particularly naturally, although opinions may differ here - I found one blog post about Swift saying: > It’s easiest to read the above code as: /“Guard that/ number /is greater than or equal to zero, or else, return nil.”/ See how that reads quite naturally? [Reinder de Vries - https://learnappmaking.com/swift-guard-let-statement-how-to/] One thing I'd like to see on proposals like this is more explicit examples comparing to current language features. For instance, why would (or shouldn't) someone use: * "guard( condition ) else { throw new Exception; }" instead of "assert( condition );"? * "guard ( condition ) else { return; }" instead of "if ( ! condition ) return;"? * "guard ( condition ) { complex logic }" instead of "if ( ! condition ) { complex logic }"? Regards, -- Rowan Tommins (né Collins) [IMSoP]
  110195
May 16, 2020 22:40 mike@newclarity.net (Mike Schinkel)
> On May 16, 2020, at 5:13 AM, Pavel Patapau blr@gmail.com> wrote: > > Hello everyone, > > I want to propose new syntax addition - guard statement, that executes code only if expression equals false and must contain control-flow changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement > <https://wiki.php.net/rfc/unbundle_xmlprc> > > Implementation in progress. > > I started work before this proposal https://externals.io/message/110107 and respected some moments in the RFC. > > > Thanks for consideration, > Pavel
Many others have already made comments that I agree with so won't echo as they have already been said. Two points: 1. That adding a `guard` would make it a reserved word is of significant concern to me, and looking at your examples I would ask why not simply ask that the if statement be able to omit the "true" block which could accomplish the same w/o needing to add another reserved word? e.g. if (condition) else { //this code executed only if condition equals false return|throw|goto; //or continue | break(in loop context only); } // in loop context - valid code while (condition){ if (condition) else { break; //valid } } Alternately, why not simply use what we already have, and what I do often, invert the condition? if ( ! condition) { //this code executed only if condition equals false return|throw|goto; //or continue | break(in loop context only); } // in loop context - valid code while (condition){ if ( ! condition ) { break; //valid } } Secondly, you reference this essay[1] which mentions why multiple returns are a bad idea, yet your proposal seems to want to encourage people to write more early returns, not fewer. Regarding the multiple returns, consider the following, using an if(condition)else{} syntax AND assuming that we could add support for try{} without being required to use a catch(): function example() { try { $value = null; if (condition1) else { break; } if (condition2) else { break; } if (condition3) else { break; } $value = do_work(); } return $value; } -Mike [1] https://medium.com/@scadge/if-statements-design-guard-clauses-might-be-all-you-need-67219a1a981a
  110197
May 17, 2020 13:48 algerd.blr@gmail.com (Pavel Patapau)
Thank you everyone for responses.

I've received huge amount of constructive criticism and useful feedback 
from various sources and after evaluation I've decided to withdrawn this 
RFC.

On 5/16/20 12:13 PM, Pavel Patapau wrote:
> > Hello everyone, > > I want to propose new syntax addition - guard statement, that executes > code only if expression equals false and must contain control-flow > changing code, and written a respective RFC: > > https://wiki.php.net/rfc/guard_statement > <https://wiki.php.net/rfc/unbundle_xmlprc> > > Implementation in progress. > > I started work before this proposal > https://externals.io/message/110107 and respected some moments in the RFC. > > > Thanks for consideration, > Pavel > > >