Re: Nullsafe
| From: | Marco Pivetta | Date: | Wed, 04 Nov 2020 20:17:58 +0000 |
| Subject: | Re: Nullsafe | ||
| References: | 1 2 3 4 5 6 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-112184@lists.php.net to get a copy of this message | ||
Hey,
On Wed, Nov 4, 2020, 20:39 Eugene Sidelnyk <zsidelnik@gmail.com> wrote:
> Yeah... Creating null was a huge mistake.
> Now it is probably too late to fix that (maybe some new language can
> introduce that).
>
>
Y'all confusing Java's
null (billion dollar mistake) with PHP's
null.
* In Java, null|object passes the object property and parameter type
declaration, but crashes on . (-> operator in PHP). That means that the
type system is fundamentally flawed.
* In PHP, null|object does not pass the object property and parameter
type declaration, and crashes on ->
The PHP T|null type is almost equivalent to `data Maybe T = Just T |
Nothing` from typed functional languages, and does not present the same
"billion dollar mistake" flaws of Java.
It is also an accurate and sufficient representation of "absence of value".
That's why I linked the toString() vs (string) example in
https://github.com/ShittySoft/symfony-live-berlin-2018-doctrine-tutorial/pull/3#issuecomment-460441229
Also, this was previously discussed in a similar way in
https://externals.io/message/108369#108386
But what do you think about introducing special class NullObject?
>
You can really only apply the null object pattern (you can use the one from
https://github.com/Ocramius/ProxyManager/blob/2.9.1/docs/null-object.md,
which is relatively type-safe) when interactions with such an object
leading to no effect are valid.
In the example I wrote above:
Let's assume following repository signature:
```php
interface Accounts
{
/** @throws AccountNotFoundException */
public function get(AccountHolder $id): Account;
}
```
In following usage:
```php
$accounts->get($receiver)
->addFunds(
$accounts->get($sender)
->detractFunds($amount)
);
```
Assuming -> were capable of operating with object|null (OP proposal),
if (by accident) somebody decided to change this signature:
```diff
interface Accounts
{
- /** @throws AccountNotFoundException */
- public function get(AccountHolder $id): Account;
+ public function get(AccountHolder $id): ?Account;
}
```
In this scenario, what can happen is that money disappears, as I've written
above:
> In the above scenario, if the first $accounts->get() call
> returns
null for any reason, you may actually destroy money (funds detracted, but
never added to another account).
The same would happen if a NullObject were used:
```diff
interface Accounts
{
- /** @throws AccountNotFoundException */
- public function get(AccountHolder $id): Account;
+ public function get(AccountHolder $id): NullObject<Account>|Account;
}
```
Same scenario (slight variation):
> In the above scenario, if the first $accounts->get() call
> returns `
NullObject<Account> ` for any reason, you may actually destroy money (funds
detracted, but never added to another account).
I would say that embracing nullability **in a type-safe manner** (please
use https://psalm.dev/ or https://phpstan.org/) leads to proper usage of
null, where it correctly indicates cases where no value has been
produced/found/calculated/etc.
Null pointer exceptions are trivially avoided by using any of these
relatively stable and well adopted static analysis tools, and we don't
really need to fight windmills at all.