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.