Re: [PHP-DEV] [RFC] anti-coalescing-operator

  107688
October 25, 2019 02:36 dohpaz@gmail.com (Ken Stanley)
This got rejected for being too large???

On Thu, Oct 24, 2019 at 10:05 PM Ken Stanley <dohpaz@gmail.com> wrote:

> Now we’re talking. :-) > > This example is better indeed. >> But here I would prefer to have the ?-> operator proposed earlier >> (nullsafe calls). >> >> return $this->user?->getName(); >> > > Sure, but that limits you to use cases on the same object. With !?? You’d > be free to both use the same object, OR to use a different construct based > on a semantically related (but technically different) construct: $userId > !?? $this->getuser(); — It’s an example, so don’t get too hung up on the > color of the bike shed. > > >> >> >> >> 3. One purpose of the operator should be that you don't have to repeat >> >> the variable. Here you do, e.g. $_SERVER['fname'] >> >> >> > >> > I'm not sure how that's necessarily a bad thing. Would you elaborate? >> Is it simply a matter of writing the same characters twice? How is that >> different than: >> > >> > if (isset($_SERVER['fname'])) { >> > $user->setName($_SERVER['fname']); >> > } >> >> I thought you like brevity? "terse and simple"? >> Ofc it is not just about typing more characters, but about having two >> places that need to be updated if we change the value expression. >> All the arguments for DRY apply. >> > > To me, and I respect if this isn’t a popular opinion, DRY applies to > processes, logic, and constructs. Things like variables and non-logic is > (again imo) a step too far. > > In other words, the repetition of a variable name in a finite scope as > intended for this operator (ie within a line or two of each other) should > not be an argument for being DRY. > > Again, > > if (isset($_POST[‘field’]) { > $this->doSomething($_POST[‘field’]); > } > > Is absolutely no different than > > $_POST[‘field’] !?? $this->doSomething($_POST[‘field’]); > > And the same goes for $foo !?? $foo->getBar(); . > > >> And yes, with existing if/else code we would also have this kind of >> repetition for this use case. >> But if we introduce a new operator, we would expect this to go away, >> wouldn't we? > > > No. The aim is not to reduce variable usage. It’s to reduce boiler plate > code for a very common, and unnecessarily verbose coding pattern. In other > words, it’s syntactic sugar. > > Yes, I appreciate that “syntactic sugar” may be considered as a > four-letter word by some, but that does not dismiss the usefulness of such > syntax. > > The fact that it’s a counterpart to an existing syntax l, I believe, > further warrants it’s inclusion into the language. Why should t we have the > ability to do something when a value is not null? Why should we be forced > to be verbose about it? > > I refuse to accept the “this can be done using if’s or &&” argument, > simply because operators like ?? and ?: already exist. > > >> The repetition becomes more relevant if the expression we would repeat >> is really long: >> >> isset($something['something']['something']['something']) !?? >> $something['something']['something']['something']->foo(); > > > This would be invalid because isset() returns Boolean. ?? deals with > bulls, and naturally !?? deals with non-nulls. You’re thinking ?: which > deals with truthy/falsey values. That may be a separate (future) RFC. ;-) > > >> >> > >> >> >> >> 1. >> >> If you would simply omit the line breaks in the first version, you >> >> would get this: >> >> >> >> if (isset($_SERVER['fname'])) $user->setName($_SERVER['fname']); >> >> if (isset($_SERVER['lname'])) $user->setName($_SERVER['lname']); >> >> if (isset($_SERVER['mname'])) $user->setName($_SERVER['mname']); >> >> if (isset($_SERVER['phone'])) $user->setName($_SERVER['phone']); >> >> if (isset($_SERVER['email'])) $user->setName($_SERVER['email']); >> > >> > >> > Ugh! I just noticed that I mistakenly did not update the method names >> for each line, so I can see how this might look like flow control. Those >> methods should be setFirstName, setLastName, setMiddleName, setPhone, and >> setEmail respectively. My apologies. >> >> I meant "only control flow" in the sense that we are not returning a >> value, or rather, we do not read the value. >> Ofc it makes a more lively example to have different methods, but I >> was looking past that already :) > > > Well thank you for the latitude. As much as I’d like to have written a > perfect informal proposal, I’m not that lucky. :-) > > As far as flow control, let’s make no mistake, ??, ?:, and the idea of !?? > are all succinct forms of flow control. To pretend otherwise is a bit > naive. Their entire existence is purely syntactic sugar so we do not have > keep writing the same boiler plate over and over. > > The goal here is to merely introduce a natural progression of ?? by > offering the ability to do something when a non-null value exists. I > understand it’s niche, but so is ?? for that matter. > > > >> >> > >> >> >> >> >> >> 2. >> >> Instead of "abusing" your new operator, you could simply "abuse" the >> >> old ternary ?: instead: >> >> >> >> !isset($_SERVER['fname']) ?: $user->setName($_SERVER['fname']); >> >> !isset($_SERVER['lname']) ?: $user->setName($_SERVER['lname']); >> >> !isset($_SERVER['mname']) ?: $user->setName($_SERVER['mname']); >> >> !isset($_SERVER['phone']) ?: $user->setName($_SERVER['phone']); >> >> !isset($_SERVER['email']) ?: $user->setName($_SERVER['email']); >> >> >> > >> > Re; #1 and #2 here: the same argument can be made for both ?? and ?:, >> which again, has already passed muster and set precedent. >> > >> >> As before, they were not really designed for this case. >> But if we do use them, they already go a long way. >> >> >> >> >> 3. >> >> One way to not repeat the variable would be to introduce a temporary >> >> local variable, like so: >> >> >> >> if (NULL !== $fname = $_SERVER['fname'] ?? NULL) >> $user->setName($fname); >> > >> > The entire point of !?? would be to keep things terse and simple in >> nature. Does it fit every possible use case? No. But neither do ?? and ?:. >> And, like ?? and ?:, no one is required to use it if they feel being more >> verbose makes sense for their needs. >> >> > >> >> >> >> >> >> So, the operator would break out of the current expression context, >> >> and produce a value one level up, a bit like a try/throw/catch would, >> >> or like a break in switch. >> >> >> > >> > Isn't this the same as the aforementioned null-safe operator [ >> https://wiki.php.net/rfc/nullsafe_calls]? >> >> Except that ?-> only works for method chaining, not for function args. >> (as pointed out in a previous message) > > > You’re right. I didn’t think my counter argument all the way through in > your given example. But, for the sake of argument, at first glance your > syntax makes zero sense because it’s (to me, a PHP and JS dev) not a common > paradigm. At least putting a ! in front of ?? has a natural association and > does not require any esoteric knowledge. > > >> >> -- Andreas >> >> > >> >> >> >> This is just a basic idea, it still leaves a lot of questions open. >> >> If the expression context is multiple levels deep, how many of these >> >> levels are we breaking? >> >> >> >> I am not suggesting this is a good idea, but I think it is an >> >> improvement to the original proposal. >> >> >> >> -- Andreas >> > >> > >> > >> > Thank you for the great feedback! > > > > So far it seems like the biggest concern(s) is that we would most likely > repeat the use of variables (nothing new), and that there are other ways to > do the same thing (again, nothing new). And of course the most valid > argument of all: adding more operators to the soup mix. Personally, I don’t > see these as show stoppers, at least given that the idea is not too wild > and out there. > > I am going to assume at this point I should have enough feedback to > warrant putting together a thorough RFC. But don’t let that stop anybody > else from chiming in. I welcome the additional feedback. > > Seriously, thank you all for taking the time to help me defend my thesis > (if you will). > > - Ken Stanley > >> -- > "Do not go gentle into that good night. Rage, rage against the dying of > the light." — Dylan Thomas > --
"Do not go gentle into that good night. Rage, rage against the dying of the light." — Dylan Thomas
  107691
October 25, 2019 09:35 rowan.collins@gmail.com (Rowan Tommins)
On Fri, 25 Oct 2019 at 03:37, Ken Stanley <dohpaz@gmail.com> wrote:

> This got rejected for being too large??? >
If you trim the quotes in your replies to just the parts needed for context, it keeps the message shorter, and makes it much easier to read through.
> > The fact that it’s a counterpart to an existing syntax l, I believe, > > further warrants it’s inclusion into the language. >
As I mentioned before, it doesn't feel to me a very natural complement. The use cases for the new operator don't feel like "the negation" of the use cases for the existing ?? operator, and although different from null-safe calls, have more in common with them than "coalescing".
> >> The repetition becomes more relevant if the expression we would repeat > >> is really long: > >> > >> isset($something['something']['something']['something']) !?? > >> $something['something']['something']['something']->foo(); > > > > > > This would be invalid because isset() returns Boolean
I suspect that was just a mistake; the point was the new operator doesn't save any repetition in an expression such as: $something['something']['something']['something'] !?? $something['something']['something']['something']->foo(); Which in this particular case could be rewritten if we had a null-safe call operator, and would gain a lot more readability: $something['something']['something']['something']?->foo();
> > As far as flow control, let’s make no mistake, ??, ?:, and the idea of > !?? > > are all succinct forms of flow control. To pretend otherwise is a bit > > naive. >
I think you're misunderstanding what people mean by "flow control"; the key point is whether you're using the operator to obtain a value, or to trigger a side effect. For instance, this would not generally be considered "flow control": $x = $someFlag ? 1 : 2; Yes, strictly speaking, you're selecting one of two paths, but the only side-effect is an assignment, outside the expression. Compare to this, where the value of the expression is never even used, and you're just choosing a side-effect: $someFlag ? deleteUser() : logOut(); Your second example was better in this respect, because it used the result: $user = $application->getUser() !?? $this->getUser(); However, is this actually the desired code? If I'm not mistaken, it would de-sugar to this: if ( isset($application->getUser() ) { $user = $this->getUser(); } else { $user = null; } Given that your specified aim is to look up a default, isn't this actually a case for the existing null-coalesce operator? $user = $application->getUser() ?? $this->getUser(); // or if the precedence is the other way around: $user = $this->getUser() ?? $application->getUser(); Regards, -- Rowan Tommins [IMSoP]
  107698
October 25, 2019 13:18 claude.pache@gmail.com (Claude Pache)
> Le 25 oct. 2019 à 04:36, Ken Stanley <dohpaz@gmail.com> a écrit : > > So far it seems like the biggest concern(s) is that we would most likely > repeat the use of variables (nothing new), and that there are other ways to > do the same thing (again, nothing new). And of course the most valid > argument of all: adding more operators to the soup mix. Personally, I don’t > see these as show stoppers, at least given that the idea is not too wild > and out there.
Hi, If you write A !?? B instead of isset(A) ? B : null you gain a *fixed* amount of 12 characters. The gain is proportionally low when A and B are not trivial expressions. (The difference is fixed, because the proposed operator does not allow to reduce possible repetition of expressions.) I don’t think that you gain readability or clarity. In particular, I am particularly confused with your last example:
> /** > * @ParamConverter(name=“application”, ...) > */ > public function myAction(Request $request, Application $application) > { > $user = $application->getUser() !?? $this->getUser(); > > // ... do something with user, without worrying about it being null. > }
because if $application->getUser() is null, then $user will be null, and you do have to worry about it being null, which makes me wonder whether there is some bug here. If it is really what you meant, spelling *null* explicitly will make it clearer, despite of being 12 characters longer. And if it is not what you meant, the bug will appear more readily: /** * @ParamConverter(name=“application”, ...) */ public function myAction(Request $request, Application $application) { $user = isset($application->getUser()) ? $this->getUser() : null; // ... do something with user, without worrying about it being null. (sic) } ―Claude