Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:107684 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 45575 invoked from network); 25 Oct 2019 02:40:49 -0000 Received: from unknown (HELO php-smtp3.php.net) (208.43.231.12) by pb1.pair.com with SMTP; 25 Oct 2019 02:40:49 -0000 Received: from php-smtp3.php.net (localhost [127.0.0.1]) by php-smtp3.php.net (Postfix) with ESMTP id 5C04B2D1FCF for ; Thu, 24 Oct 2019 17:27:20 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on php-smtp3.php.net X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.2 X-Spam-ASN: AS3215 2.6.0.0/16 X-Spam-Virus: No Received: from mail-qk1-x72b.google.com (mail-qk1-x72b.google.com [IPv6:2607:f8b0:4864:20::72b]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by php-smtp3.php.net (Postfix) with ESMTPS for ; Thu, 24 Oct 2019 17:27:19 -0700 (PDT) Received: by mail-qk1-x72b.google.com with SMTP id e66so158321qkf.13 for ; Thu, 24 Oct 2019 17:27:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=dqxtech-net.20150623.gappssmtp.com; s=20150623; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc:content-transfer-encoding; bh=7Em7KDOmU86O7XMNcCu6ZyKxy+BHCed86/7TZqyr630=; b=lsGG2MKjczkJTca51n9snD7wmid8qtPWDQrwuEaWOP66PDVCtwc05EbRmQpdu+Yf7N qgPpoIOx+OU6KX+h+3Ud7JQ7HNuA3mOFrGI0Z+X05o3I51hF1YqLTpzqfLBnZlnT3r6Q xYh8gYdsZGhMlftuMk43vwtNhLNofPiM9ObiyNRY5IsCp8glPruJGHVIl9Jkhvsz7j9r ExUyA7USTLyHHjKznCdTasP9Zw2XSwX0Ug7TYif7WWj4L1B2QphMFxDr5NK0HWfrlZE5 cOl7FJWBgopEwmbgexi3pmCzuCSIl68urpQcxY+HI/XsE8aZmQBy4f81Uo1RRpaeBSqV KrjA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc:content-transfer-encoding; bh=7Em7KDOmU86O7XMNcCu6ZyKxy+BHCed86/7TZqyr630=; b=N1aPrGTasD9AeGA1Dz4zSBXwzub5a+P+xdBkMlVudyYYWu0L4gla7dXydnhVxx8MSP CqfaqT6oS/NGSAHBCvN5kQHgYXPMFSIVMe7ZmFM0CFAhGSZnqvECnL3+D53CnjZfBuBr HPtuxE4zaZBPPM6A+/vMS8OMmX4fcmEDSGYrjzYcWxd10i0eVi0ARQVwMCbnKWmbMhRu wzVs57Pgd0jhcg+afuFcMP4SD09+IRv7vW6xXlKoN+wP6/N6TsOGMFYQkvOGk3eT2592 JHBi76gdKT5/q3LJeH6QCd4gJsisIq/s6mvhvMLNkg5e3VtKiULuv2885DriqXA0RQJt TEfg== X-Gm-Message-State: APjAAAX7smTqqJeI2gRvM7dPqdXSpCdoCZyDe0WgzaI6oB2S2xMhCl/f LsKMCfGBJAVl16YdMZr9MIbQbqJMyeU= X-Google-Smtp-Source: APXvYqwESnrgsSoZMurSiEfGpqMy9FsFocdxP7FKgiAFkxz24pXZkyGyEaiioLmnM0FgM4BFHieCtw== X-Received: by 2002:a37:63d6:: with SMTP id x205mr492320qkb.25.1571963238594; Thu, 24 Oct 2019 17:27:18 -0700 (PDT) Received: from mail-qt1-f172.google.com (mail-qt1-f172.google.com. [209.85.160.172]) by smtp.googlemail.com with ESMTPSA id x6sm229706qts.37.2019.10.24.17.27.17 for (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 24 Oct 2019 17:27:17 -0700 (PDT) Received: by mail-qt1-f172.google.com with SMTP id t20so647548qtr.10 for ; Thu, 24 Oct 2019 17:27:17 -0700 (PDT) X-Received: by 2002:a05:6214:1887:: with SMTP id cx7mr689543qvb.123.1571963236900; Thu, 24 Oct 2019 17:27:16 -0700 (PDT) MIME-Version: 1.0 References: In-Reply-To: Date: Fri, 25 Oct 2019 02:27:05 +0200 X-Gmail-Original-Message-ID: Message-ID: To: Ken Stanley Cc: Dan Ackroyd , PHP internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Envelope-From: Subject: Re: [PHP-DEV] [RFC] anti-coalescing-operator From: andreas@dqxtech.net (Andreas Hennings) On Thu, 24 Oct 2019 at 23:49, Ken Stanley wrote: > > On Thu, Oct 24, 2019 at 4:29 PM Andreas Hennings wr= ote: >> >> On Thu, 24 Oct 2019 at 20:59, Ken Stanley wrote: >> > >> > On Thu, Oct 24, 2019 at 1:33 PM Dan Ackroyd w= rote: >> > >> > > On Thu, 24 Oct 2019 at 18:21, Ken Stanley wrote: >> > > > >> > > > Since PHP 7.0 brought forward the Null Coalescing Operator (??), w= riting >> > > > more succinct code for how to handle null values has been a blessi= ng. >> > > But, >> > > > what about the inverse when you want to do something when a value = is not >> > > > null? >> > > >> > > Hi Ken, >> > > >> > > It may help to give a real world example, rather than a metasyntacti= c >> > > one, as I can't immediately see how this would be useful. >> > > >> > > People have been expressing a concern over 'symbol soup' for similar >> > > ideas. The null colalesce scenario happens frequently enough, that i= t >> > > seemed to overcome the hurdle needed for acceptance. Again, giving a >> > > real world example of what you currently need to do frequently might >> > > help other people understand the need. >> > > >> > > cheers >> > > Dan >> > > >> > >> > Hi Dan, >> > >> > After some thought, and searching through my existing code bases, I be= lieve >> > I've come up with a decent code example to help demonstrate the useful= ness >> > of the proposed anti-coalescing-operator: >> > >> > Without !??: >> > > > >> > class ExampleController >> > { >> > /** >> > * PATCH a User object. >> > */ >> > public function saveAction(int $userId) >> > { >> > $user =3D $this->getUser($userId); >> > >> > 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']); >> > } >> > >> > $this-saveUser($user); >> > } >> > } >> > >> > With !??: >> > > > >> > class ExampleController >> > { >> > /** >> > * PATCH a User object. >> > */ >> > public function saveAction(int $userId) >> > { >> > $user =3D $this->getUser($userId); >> > >> > $_SERVER['fname'] !?? $user->setName($_SERVER['fname']); >> > $_SERVER['lname'] !?? $user->setName($_SERVER['lname']); >> > $_SERVER['mname'] !?? $user->setName($_SERVER['mname']); >> > $_SERVER['phone'] !?? $user->setName($_SERVER['phone']); >> > $_SERVER['email'] !?? $user->setName($_SERVER['email']); >> > >> > $this-saveUser($user); >> > } >> > } >> > Thank you, >> > Ken Stanley >> >> Not convinced. >> 1. Most of the perceived brevity is from omitting line breaks and >> curly brackets, which is a bit misleading imo. > > > This argument can be made about ?? And ?:, which have already passed must= er and creates a precedent. But these were designed to produce a value, not for just control flow - see my other point. Yes, the same argument could have been made for those, if a similar example had been given to advertise their introduction. > Additionally, this is meant to compliment the existing ?? by adding a neg= ation counterpart (similar to how =3D=3D has !=3D=3D and > has <). > > I=E2=80=99m curious to what you find misleading about it? Its meant to li= terally be the not-null coalescing operator. Misleading as in "look how much shorter this is". > >> >> 2. It is not the intended use of these kinds of operators (ternary or >> null coalesce). Normally you would use them to produce a value, here >> you use them for control flow only. > > > Here is another example, not using them for flow control: > > > class Foo > { > /** > * @return User|null > */ > private $user; > > /** > * @return string|null > */ > public function getName(): ?string > { > return $this->user !?? $this->user->getName(); > } > } > > compared to: > > class Foo > { > /** > * @return User|null > */ > private $user; > > /** > * @return string|null > */ > public function getName(): ?string > { > // or $this->user instanceof User > return $this->user !=3D=3D null ? $this->user->getName() : null; > } > } This example is better indeed. But here I would prefer to have the ?-> operator proposed earlier (nullsafe calls). return $this->user?->getName(); >> >> 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 differ= ent 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. 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? The repetition becomes more relevant if the expression we would repeat is really long: isset($something['something']['something']['something']) !?? $something['something']['something']['something']->foo(); > >> >> 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 metho= ds should be setFirstName, setLastName, setMiddleName, setPhone, and setEma= il 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 :) > >> >> >> 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 ?:, whi= ch 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 !=3D=3D $fname =3D $_SERVER['fname'] ?? NULL) $user->setNam= e($fname); > > The entire point of !?? would be to keep things terse and simple in natur= e. 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 verbos= e makes sense for their needs. > >> >> >> This gets more useful if the variable expression is something longer. >> >> A new language feature for this purpose could have an anatomy like this: >> https://3v4l.org/TjuuO or >> https://3v4l.org/U6arm >> >> and the short syntax would be like so: >> >> $product =3D ($x ??! NULL) * ($y ??! NULL); >> >> or the NULL can be omitted: >> >> $product =3D ($x ??!) * ($y ??!); > > > Not sure if the ??! was on purpose as an alternative to !??, but given ho= w other operators put the negation operator first, I feel we should maintai= n the common standard. I don't really think of it as a "negated ??". The motivation to have a negation seems quite theoretical to me. I am more interested in actual use cases. > >> >> >> 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://wik= i.php.net/rfc/nullsafe_calls]? Except that ?-> only works for method chaining, not for function args. (as pointed out in a previous message) -- 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!