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
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
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
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
Hi Pavel
https://wiki.php.net/rfc/guard_statement
https://wiki.php.net/rfc/unbundle_xmlprc
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
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; }
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_xmlprcImplementation 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
Pavel,
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_xmlprcImplementation 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
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_xmlprcImplementation 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
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:
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]
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_xmlprcImplementation 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:
- 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
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.
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_xmlprcImplementation in progress.
I started work before this proposal
https://externals.io/message/110107 and respected some moments in the RFC.Thanks for consideration,
Pavel