Hey,

On Wed, Nov 4, 2020, 20:39 Eugene Sidelnyk <zsidel...@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.

Reply via email to