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 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
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/126826Regards,
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
I'm not sure it justifies a new pseudo-function language construct.
Fair enough. Thank you for the feedback.
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.
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]
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
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]
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.
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.
-
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. -
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')
- 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]
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.
- 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