Re: [PHP-DEV] Feedback for nullc Idea

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.

Regards,
Kamil

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.
Feature: Null Coalescing Variable Handling Construct - Externals

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

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.

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]

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

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]

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.