Feedback for nullc Idea

php.internals

Robert Chapin

1 year ago
Hi PHP folks, I submitted a proposal on Monday that received no replies.  The idea was about adding a functional construct nullc() that could act as an inline null coalesce for a variable.  That task is currently not possible in a nullc() user function, but instead requires an expression like ($var ?? null) with mandatory extra parentheses.  A functional expression could make this less prone to mistakes.  It would also avoid concerns with alternatives such as unary operators. Any feedback on this idea would be appreciated. ------------- Robert Chapin

Kamil Tekiela

1 year ago
On Sun, 23 Mar 2025 at 12:52, Robert Chapin <php@miqrogroove.com> wrote:
> > Hi PHP folks, > > I submitted a proposal on Monday that received no replies. The idea was > about adding a functional construct nullc() that could act as an inline > null coalesce for a variable. That task is currently not possible in a > nullc() user function, but instead requires an expression like ($var ?? > null) with mandatory extra parentheses. A functional expression could > make this less prone to mistakes. It would also avoid concerns with > alternatives such as unary operators. > > Any feedback on this idea would be appreciated. > > ------------- > Robert Chapin
Hi Robert, For some reason, your emails are classified as spam. I am attaching the link to your previous email for the record. https://externals.io/message/126826 Regards, Kamil

Larry Garfield

1 year ago
On Sun, Mar 23, 2025, at 8:37 AM, Kamil Tekiela wrote:
> On Sun, 23 Mar 2025 at 12:52, Robert Chapin <php@miqrogroove.com> wrote: >> >> Hi PHP folks, >> >> I submitted a proposal on Monday that received no replies. The idea was >> about adding a functional construct nullc() that could act as an inline >> null coalesce for a variable. That task is currently not possible in a >> nullc() user function, but instead requires an expression like ($var ?? >> null) with mandatory extra parentheses. A functional expression could >> make this less prone to mistakes. It would also avoid concerns with >> alternatives such as unary operators. >> >> Any feedback on this idea would be appreciated. >> >> ------------- >> Robert Chapin > > Hi Robert, > > For some reason, your emails are classified as spam. I am attaching > the link to your previous email for the record. > https://externals.io/message/126826 > > Regards, > Kamil
While I can somewhat see the use case, I'd argue that if you have a lot of that use case going on then you should likely rethink your data structures. (Eg, fewer arrays.) That's not always possible at the HTTP boundary (queries are arrays, of course), but if it becomes onerous, there are better ways. I'm not sure it justifies a new pseudo-function language construct. --Larry Garfield

Robert Chapin

1 year ago
On 3/23/2025 11:10 AM, Larry Garfield wrote:
> I'm not sure it justifies a new pseudo-function language construct.
Fair enough.  Thank you for the feedback. On 3/23/2025 2:37 PM, Rowan Tommins [IMSoP] wrote:
> It's telling that all your examples with ?? put a value other than > null on the right-hand side.
I may have over-simplified the examples.  Comparing $input === 'yes' will have the same result whether $input is null or 'none' or an empty string.  So not implying a result type, just want to compare a literal or other variable to $input even when not declared. A different example could be if (coalesce($_POST['tick']) > 10) return;
> In my experience, a lot of people struggle to follow logic like this, > and it can easily lead to subtle bugs. Compare the same logic with > explicit defaults: > > if (nullc($test, 'off') === 'on') > if (nullc($test, 'on') !== 'off')
Having optional defaults seems fine.  Meanwhile... if ($test ?? 'on' !== 'on') will evaluate to true when $test is set to 'on'.
> There is a small risk that some people might have an existing function > with different behaviour, e.g. a test on "emptiness" with `$arg != > false`, and need to rename it when upgrading PHP.
This is a good point.  I assume that risk exists for all new functions and needs to be considered.
> I'm still only lukewarm on including it, because it's mostly just > reminding you to include a pair of parentheses:
Think of it as a quick way to use an undeclared variable without having to explain operator precedence to other developers. In any case, thank you for the feedback.

Rowan Tommins [IMSoP]

1 year ago
On 25 March 2025 05:53:30 GMT, Robert Chapin <php@miqrogroove.com> wrote:
>I may have over-simplified the examples.  Comparing $input === 'yes' will have the same result whether $input is null or 'none' or an empty string.  So not implying a result type, just want to compare a literal or other variable to $input even when not declared.
The examples are fine, I think, but perhaps I didn't explain mine clearly enough. My point is that because null is not going to be coerced by the language to either 'on' nor 'off', there's an implied default depending how you write the expression. The following both evaluate to true for an input of 'on', and false for an input of 'off', but give different results for null: $input === 'on' $input !== 'off' The implied default in the first is 'off', but in the second it's 'on'.
>A different example could be if (coalesce($_POST['tick']) > 10) return;
In this case, the implied default is 0. It's less risky, because the coercion from null to int is straightforward. The following all imply a default of zero: if (coalesce($_POST['tick']) >= 10) return; if (coalesce($_POST['tick']) != 10) return; if (coalesce($_POST['tick']) !== 10) return; if (coalesce($_POST['tick']) > 0) return; if (coalesce($_POST['tick']) >= 0) return; if (coalesce($_POST['tick']) != 0) return; But this doesn't: if (coalesce($_POST['tick']) !== 0) return; By specifying the default explicitly, we don't have to examine the expression carefully to see what's implied. I don't know if I'd go as far as banning a single-argument coalesce, but I would definitely discourage its use. Rowan Tommins [IMSoP]

Robert Chapin

1 year ago
On 3/25/2025 4:45 AM, Rowan Tommins [IMSoP] wrote:
> My point is that because null is not going to be coerced by the language to either 'on' nor 'off', there's an implied default depending how you write the expression.
That kind of user mistake is hard for me to wrap my mind around.  I don't expect a missing variable to be identical to a string literal.  So a unary coalesce seems perfectly natural to me.
> The implied default in the first is 'off', but in the second it's 'on'.
I thought the implied default was null.  $input === 'on' is only identical for 'on'.  $input !== 'off' is always not identical unless 'off'.
>> A different example could be if (coalesce($_POST['tick']) > 10) return; > In this case, the implied default is 0.
I thought the implied default was null.  For me, potential confusion would arise in situations where a database query might return a null value and I have to choose between is_null() or coalesce() or ($a ?? "").  That's the situation where returning null doesn't accomplish anything and the single-var coalesce becomes the wrong choice.
> I don't know if I'd go as far as banning a single-argument coalesce, but I would definitely discourage its use.
The feedback and the thoughtful perspective on user mistakes are helpful.  Thank you again. ------------- Robert Chapin

Rowan Tommins [IMSoP]

1 year ago
On 25 March 2025 16:42:45 GMT, Robert Chapin <php@miqrogroove.com> wrote:
>On 3/25/2025 4:45 AM, Rowan Tommins [IMSoP] wrote:
>> The implied default in the first is 'off', but in the second it's 'on'. >I thought the implied default was null.
By "implied", I'm talking about the observed behaviour: if you give it null, does it *act the same as* when you give it 'off', or when you give it 'on'?
>>> A different example could be if (coalesce($_POST['tick']) > 10) return; >> In this case, the implied default is 0. > >I thought the implied default was null. 
In this case it's more than implied, the engine is literally converting the value to zero. The logical answer to the question "is null greater than ten?" is "null", as used by SQL. e.g. - "I don't know how many apples are in the box." (set num_apples=null) - "Are there more than ten apples in the box?" (query: num_apples>10) - "I don't know." (result: null) The reason you don't get that answer in PHP is that it looks at the context and "type juggles" the null to an integer, and gives you the answer to "is zero greater than ten?" instead. (In this specific case, you can't observe the difference, because null would itself be juggled to false by the if statement; but substitute "less than ten", and you get true in PHP, still null in SQL.) Using coalesce($foo) in this example means the reader has to remember or guess the type juggling rules to know that it's equivalent to (int)($foo ?? null), and that (int)null will give 0. Writing coalesce($foo, 0) or ($foo ?? 0) is a small cost when writing the code to save cost every time anyone reads it. Rowan Tommins [IMSoP]

Robert Chapin

1 year ago
On 3/25/2025 4:11 PM, Rowan Tommins [IMSoP] wrote:
> Writing coalesce($foo, 0) or ($foo ?? 0) is a small cost when writing the code to save cost every time anyone reads it.
I see, part of the concern is just avoiding implicit type coercion.  Yes, that would be an understandable reason to require a second value, which might limit the overall flexibility of the feature.

Rowan Tommins [IMSoP]

1 year ago
On 23/03/2025 12:52, Robert Chapin wrote:
> Hi PHP folks, > > I submitted a proposal on Monday that received no replies.  The idea > was about adding a functional construct nullc() that could act as an > inline null coalesce for a variable.  That task is currently not > possible in a nullc() user function, but instead requires an > expression like ($var ?? null) with mandatory extra parentheses.
I'm sympathetic to the problem you're trying to solve - the precedence of ?? isn't always helpful - but I'm not sure I like the proposed solution, for 3 reasons. 1) The name "nullc" is over-shortened and cryptic. The "c" looks almost like an accident, and it doesn't actually do anything if given a null. 2) With only one argument, it isn't really "coalescing" anything, it's just suppressing errors. It's telling that all your examples with ?? put a value other than null on the right-hand side. If the default is not one of the normal values, the effective default depends on how you use it: if (nullc($test) === 'on') if (nullc($test) !== 'off') In my experience, a lot of people struggle to follow logic like this, and it can easily lead to subtle bugs. Compare the same logic with explicit defaults: if (nullc($test, 'off') === 'on') if (nullc($test, 'on') !== 'off') 3) The function-like syntax doesn't seem to gain us much; it has to be a language construct not a true function, so it won't be usable with things like array_map, and with two arguments, nullc($foo, $bar) and ($foo ?? $bar) look very similar. All of which makes me look to the COALESCE function from SQL: - The name is short but not cryptically abbreviated, and will be familiar to most PHP users. - The function is variadic - you can pass just one argument, but can also pass three, or ten. - The parameters are evaluated lazily, like they would be with an operator. I've seen and implemented many variations of this in userland, although obviously using `$arg !== null` rather than `isset($arg)`. There is a small risk that some people might have an existing function with different behaviour, e.g. a test on "emptiness" with `$arg != false`, and need to rename it when upgrading PHP. I'm still only lukewarm on including it, because it's mostly just reminding you to include a pair of parentheses: if (($_POST['input'] ?? null) === 'yes') echo 'success'; if (coalesce($_POST['input']) === 'yes') echo 'success'; if (coalesce($_POST['input'], null) === 'yes') echo 'success'; if (($_POST['input'] ?? $_GET['input'] ?? 'N/A') !== 'N/A') echo 'meaningful value provided'; if (coalesce($_POST['input'], $_GET['input'], 'N/A') !== 'N/A') echo 'meaningful value provided';
-- Rowan Tommins [IMSoP]

Aimeos | Norbert Sendetzky

1 year ago
On 23.03.25 19:37, Rowan Tommins [IMSoP] wrote:
> I'm sympathetic to the problem you're trying to solve - the precedence > of ?? isn't always helpful - but I'm not sure I like the proposed > solution, for 3 reasons. > > 1) The name "nullc" is over-shortened and cryptic. The "c" looks almost > like an accident, and it doesn't actually do anything if given a null.
To me, a wider approach might be more helpful, i.e. using "get" with an optional default value: if(get($a['val'])) // equals if($a['val] ?? null) if(get($a['val'], true)) // equals if($a['val] ?? true) if(get($a['val], 'yes')) // equals if($a['val] ?? 'yes') Best,
-- Norbert Sendetzky Aimeos GmbH Rennbahnstr. 32 DE-22111 Hamburg E-Mail: norbert@aimeos.com Phone: +49 40 8668 4492 Web: aimeos.com Trade register: District court Hamburg HRB 143090 Managing director: Norbert Sendetzky VAT ID: DE302287839