On Thu, Oct 24, 2019 at 4:29 PM Andreas Hennings <andr...@dqxtech.net>
wrote:

> On Thu, 24 Oct 2019 at 20:59, Ken Stanley <doh...@gmail.com> wrote:
> >
> > On Thu, Oct 24, 2019 at 1:33 PM Dan Ackroyd <dan...@basereality.com>
> wrote:
> >
> > > On Thu, 24 Oct 2019 at 18:21, Ken Stanley <doh...@gmail.com> wrote:
> > > >
> > > > Since PHP 7.0 brought forward the Null Coalescing Operator (??),
> writing
> > > > more succinct code for how to handle null values has been a blessing.
> > > 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 metasyntactic
> > > 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 it
> > > 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
> believe
> > I've come up with a decent code example to help demonstrate the
> usefulness
> > of the proposed anti-coalescing-operator:
> >
> > Without !??:
> > <?php
> >
> > class ExampleController
> > {
> >     /**
> >      * PATCH a User object.
> >      */
> >     public function saveAction(int $userId)
> >     {
> >         $user = $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 !??:
> > <?php
> >
> > class ExampleController
> > {
> >     /**
> >      * PATCH a User object.
> >      */
> >     public function saveAction(int $userId)
> >     {
> >         $user = $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 muster
and creates a precedent. Additionally, this is meant to compliment the
existing ?? by adding a negation counterpart (similar to how == has !== and
> has <).

I’m curious to what you find misleading about it? Its meant to literally be
the not-null coalescing operator.


> 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:

<?php

class Foo
{
    /**
     * @return User|null
     */
    private $user;

    /**
     * @return string|null
     */
    public function getName(): ?string
    {
        return $this->user !?? $this->user->getName();
    }
}

compared to:
<?php

class Foo
{
    /**
     * @return User|null
     */
    private $user;

    /**
     * @return string|null
     */
    public function getName(): ?string
    {
        // or $this->user instanceof User
        return $this->user !== null ? $this->user->getName() : null;
    }
}


> 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']);
}


> 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.


>
> 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.


> 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.


>
> 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 = ($x ??! NULL) * ($y ??! NULL);
>
> or the NULL can be omitted:
>
>    $product = ($x ??!) * ($y ??!);
>

Not sure if the ??! was on purpose as an alternative to !??, but given how
other operators put the negation operator first, I feel we should maintain
the common standard.


>
> 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]?


> 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!

Reply via email to