Discussion - Anti-null coercion

php.internals

Robert Landers

2 years ago
Hello Internals, We currently have a null coercion operator: ??, but we lack an anti-null coercion operator. For example, if I wanted to operate on a header, if-and-only-if it exists, I'd have to write something like this one-liner: fn() => ($_SERVER['HTTP_X_MY_HEADER'] ?? null) ? md5($_SERVER['HTTP_X_MY_HEADER']) : null; Or something like this: function() { if(!empty($_SERVER['HTTP_X_MY_HEADER']) { return md5($_SERVER['HTTP_X_MY_HEADER']); } return null; } This is rather tedious when you have to do it, so, I'd like to discuss adding a new "anti-null coercion" operator: ?! This would collapse the previous verbose code into: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER']; When it is null, it will stay null, thus the above is the same as: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER'] ?? null; It would have a lower precedence than ?? so that the above line would read from left to right without requiring parenthesis/brackets. The operator would only return the right-hand side if the left-hand side exists (aka, not null), otherwise, it would return null. I'm not particularly attached to the ?! syntax (since it does, in fact, look very similar to ?:), so perhaps focusing on the merits of the idea first, then bikeshedding the syntax later would be a good approach? Thoughts? Robert Landers Software Engineer Utrecht NL

Sergii Shymko

2 years ago
On Oct 29, 2023, at 1:31 PM, Robert Landers <landers.robert@gmail.com<mailto:landers.robert@gmail.com>> wrote: Hello Internals, We currently have a null coercion operator: ??, but we lack an anti-null coercion operator. For example, if I wanted to operate on a header, if-and-only-if it exists, I'd have to write something like this one-liner: fn() => ($_SERVER['HTTP_X_MY_HEADER'] ?? null) ? md5($_SERVER['HTTP_X_MY_HEADER']) : null; Or something like this: function() { if(!empty($_SERVER['HTTP_X_MY_HEADER']) { return md5($_SERVER['HTTP_X_MY_HEADER']); } return null; } This is rather tedious when you have to do it, so, I'd like to discuss adding a new "anti-null coercion" operator: ?! This would collapse the previous verbose code into: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER']; When it is null, it will stay null, thus the above is the same as: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER'] ?? null; It would have a lower precedence than ?? so that the above line would read from left to right without requiring parenthesis/brackets. The operator would only return the right-hand side if the left-hand side exists (aka, not null), otherwise, it would return null. I'm not particularly attached to the ?! syntax (since it does, in fact, look very similar to ?:), so perhaps focusing on the merits of the idea first, then bikeshedding the syntax later would be a good approach? Thoughts? Robert Landers Software Engineer Utrecht NL
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php Hi Robert, Why don’t you combine the two examples and use a regular ternary operator along with isset/empty()? fn() => !empty($_SERVER[‘HTTP_X_MY_HEADER’]) ? md5($_SERVER[‘HTTP_X_MY_HEADER’]) : null; It’s only ever so slightly longer than the proposed ?! operator, but way more powerful. You explicitly control the evaluation expression along with the default value. Regards, Sergii

Fabio Carpi

2 years ago
Following the php logic, maybe !?? ________________________________ From: Sergii Shymko <sergey@shymko.net> Sent: Sunday, October 29, 2023 4:53:12 PM To: Robert Landers <landers.robert@gmail.com> Cc: internals <internals@lists.php.net> Subject: Re: [PHP-DEV] Discussion - Anti-null coercion On Oct 29, 2023, at 1:31 PM, Robert Landers <landers.robert@gmail.com<mailto:landers.robert@gmail.com>> wrote: Hello Internals, We currently have a null coercion operator: ??, but we lack an anti-null coercion operator. For example, if I wanted to operate on a header, if-and-only-if it exists, I'd have to write something like this one-liner: fn() => ($_SERVER['HTTP_X_MY_HEADER'] ?? null) ? md5($_SERVER['HTTP_X_MY_HEADER']) : null; Or something like this: function() { if(!empty($_SERVER['HTTP_X_MY_HEADER']) { return md5($_SERVER['HTTP_X_MY_HEADER']); } return null; } This is rather tedious when you have to do it, so, I'd like to discuss adding a new "anti-null coercion" operator: ?! This would collapse the previous verbose code into: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER']; When it is null, it will stay null, thus the above is the same as: fn() => $_SERVER['HTTP_X_MY_HEADER'] ?! md5($_SERVER['HTTP_X_MY_HEADER'] ?? null; It would have a lower precedence than ?? so that the above line would read from left to right without requiring parenthesis/brackets. The operator would only return the right-hand side if the left-hand side exists (aka, not null), otherwise, it would return null. I'm not particularly attached to the ?! syntax (since it does, in fact, look very similar to ?:), so perhaps focusing on the merits of the idea first, then bikeshedding the syntax later would be a good approach? Thoughts? Robert Landers Software Engineer Utrecht NL
-- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fwww.php.net%2Funsub.php&data=05%7C01%7C%7Cf2f8309c674c4f9137a008dbd8b8b73a%7C84df9e7fe9f640afb435aaaaaaaaaaaa%7C1%7C0%7C638342060099962597%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=Frc%2BghvwIS1q%2B79n4joMssXCfeFIHkf7mziuH28XLW0%3D&reserved=0<https://www.php.net/unsub.php> Hi Robert, Why don’t you combine the two examples and use a regular ternary operator along with isset/empty()? fn() => !empty($_SERVER[‘HTTP_X_MY_HEADER’]) ? md5($_SERVER[‘HTTP_X_MY_HEADER’]) : null; It’s only ever so slightly longer than the proposed ?! operator, but way more powerful. You explicitly control the evaluation expression along with the default value. Regards, Sergii

Robert Landers

2 years ago
On Sun, Oct 29, 2023 at 8:53 PM Sergii Shymko <sergey@shymko.net> wrote:
> > > > On Oct 29, 2023, at 1:31 PM, Robert Landers <landers.robert@gmail.com> wrote: > > Hello Internals, > > We currently have a null coercion operator: ??, but we lack an > anti-null coercion operator. > > For example, if I wanted to operate on a header, if-and-only-if it > exists, I'd have to write something like this one-liner: > > fn() => > ($_SERVER['HTTP_X_MY_HEADER'] ?? null) > ? md5($_SERVER['HTTP_X_MY_HEADER']) > : null; > > Or something like this: > > function() { > if(!empty($_SERVER['HTTP_X_MY_HEADER']) { > return md5($_SERVER['HTTP_X_MY_HEADER']); > } > > return null; > } > > This is rather tedious when you have to do it, so, I'd like to discuss > adding a new "anti-null coercion" operator: ?! > > This would collapse the previous verbose code into: > > fn() => > $_SERVER['HTTP_X_MY_HEADER'] > ?! md5($_SERVER['HTTP_X_MY_HEADER']; > > When it is null, it will stay null, thus the above is the same as: > > fn() => > $_SERVER['HTTP_X_MY_HEADER'] > ?! md5($_SERVER['HTTP_X_MY_HEADER'] > ?? null; > > It would have a lower precedence than ?? so that the above line would > read from left to right without requiring parenthesis/brackets. The > operator would only return the right-hand side if the left-hand side > exists (aka, not null), otherwise, it would return null. > > I'm not particularly attached to the ?! syntax (since it does, in > fact, look very similar to ?:), so perhaps focusing on the merits of > the idea first, then bikeshedding the syntax later would be a good > approach? > > Thoughts? > > Robert Landers > Software Engineer > Utrecht NL > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > > > Hi Robert, > > Why don’t you combine the two examples and use a regular ternary operator along with isset/empty()? > > fn() => > !empty($_SERVER[‘HTTP_X_MY_HEADER’]) > ? md5($_SERVER[‘HTTP_X_MY_HEADER’]) > : null; > > It’s only ever so slightly longer than the proposed ?! operator, but way more powerful. You explicitly control the evaluation expression along with the default value. > > Regards, > Sergii
Hey Sergii,
> Why don’t you combine the two examples and use a regular ternary operator along with isset/empty()?
Because falsy is different than null/non-existent. The closest equivalent is "array_key_exists" (instead of empty/isset) but this proposal isn't just applicable to arrays. I run into this pattern of "if not null, then do something with the value" pretty often and it is tedious to type it out. Robert Landers Software Engineer Utrecht NL

Sergii Shymko

2 years ago
On Oct 29, 2023, at 3:23 PM, Robert Landers <landers.robert@gmail.com<mailto:landers.robert@gmail.com>> wrote: Because falsy is different than null/non-existent. The closest equivalent is "array_key_exists" (instead of empty/isset) but this proposal isn't just applicable to arrays. I run into this pattern of "if not null, then do something with the value" pretty often and it is tedious to type it out. In terms of falsy values, null coalescing operator ?? is a syntactic sugar for isset(). So, ?! operator will be equivalent to !isset() expression in the ternary. For the reference: https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op

Ilija Tovilo

2 years ago
Hi Robert On Sun, Oct 29, 2023 at 7:31 PM Robert Landers <landers.robert@gmail.com> wrote:
> > Hello Internals, > > We currently have a null coercion operator: ??, but we lack an > anti-null coercion operator. > ... > fn() => > ($_SERVER['HTTP_X_MY_HEADER'] ?? null) > ? md5($_SERVER['HTTP_X_MY_HEADER']) > : null; > ... > This is rather tedious when you have to do it, so, I'd like to discuss > adding a new "anti-null coercion" operator: ?! > > This would collapse the previous verbose code into: > > fn() => > $_SERVER['HTTP_X_MY_HEADER'] > ?! md5($_SERVER['HTTP_X_MY_HEADER'];
This does not seem significantly less verbose to me. The main motivation for ?? was that it avoids repeating the expression over something like ?:. I would see a stronger argument for this feature if it offered the same benefit. E.g. $_SERVER['HTTP_X_MY_HEADER'] ?! md5($$)
> It would have a lower precedence than ?? so that the above line would > read from left to right without requiring parenthesis/brackets. The > operator would only return the right-hand side if the left-hand side > exists (aka, not null), otherwise, it would return null.
I think it should have a higher precedence. $_SERVER['HTTP_X_MY_HEADER'] ?! md5($$) ?? 'abc' ==> ($_SERVER['HTTP_X_MY_HEADER'] ?! md5($$)) ?? 'abc' Otherwise the result is NULL if the header is missing, given that the coalesce operator is never executed. That said, while I've certainly encountered this situation, it's nothing a temporary variable can't fix. I don't personally believe there's a strong need for such an operator. Ilija

Larry Garfield

2 years ago
On Mon, Oct 30, 2023, at 11:17 AM, Ilija Tovilo wrote:
> Hi Robert > > On Sun, Oct 29, 2023 at 7:31 PM Robert Landers <landers.robert@gmail.com> wrote: >> >> Hello Internals, >> >> We currently have a null coercion operator: ??, but we lack an >> anti-null coercion operator. >> ... >> fn() => >> ($_SERVER['HTTP_X_MY_HEADER'] ?? null) >> ? md5($_SERVER['HTTP_X_MY_HEADER']) >> : null; >> ... >> This is rather tedious when you have to do it, so, I'd like to discuss >> adding a new "anti-null coercion" operator: ?! >> >> This would collapse the previous verbose code into: >> >> fn() => >> $_SERVER['HTTP_X_MY_HEADER'] >> ?! md5($_SERVER['HTTP_X_MY_HEADER']; > > This does not seem significantly less verbose to me. The main > motivation for ?? was that it avoids repeating the expression over > something like ?:. I would see a stronger argument for this feature if > it offered the same benefit. E.g. > > $_SERVER['HTTP_X_MY_HEADER'] ?! md5($$) > >> It would have a lower precedence than ?? so that the above line would >> read from left to right without requiring parenthesis/brackets. The >> operator would only return the right-hand side if the left-hand side >> exists (aka, not null), otherwise, it would return null. > > I think it should have a higher precedence. > > $_SERVER['HTTP_X_MY_HEADER'] ?! md5($$) ?? 'abc' > ==> > ($_SERVER['HTTP_X_MY_HEADER'] ?! md5($$)) ?? 'abc' > > Otherwise the result is NULL if the header is missing, given that the > coalesce operator is never executed. > > That said, while I've certainly encountered this situation, it's > nothing a temporary variable can't fix. I don't personally believe > there's a strong need for such an operator. > > Ilija
Another point to note is that for objects, we already have ?->, which effectively serves this purpose. It is sugar for: is_null($o) ? null : $o->whatever() So any new operator would only be relevant for free-standing variables (which is automatically a code smell) or arrays, and I'm not convinced the latter is a large enough use case in a decently written code base. (You really should know what your data is, and the best way to do that is map it into a proper object, at which point ?-> comes into play.) --Larry Garfield