On 4/15/2016 5:50 PM, Larry Garfield wrote: > That there are a few small cases where PHP's current design makes NULL a > reasonable sentinel value (custom iterators, fread() as you mention, > etc.) does not mean that in most cases, returning ValueObject|Null is > rude and abusive to users of your API. Yes, end-of-file is not an > exceptional case so should not throw an exception. I completely agree > there. But "user not found" I'd argue is. (Or rather, if it's not an > exceptional case your data model is kinda broken to begin with, because > why are you asking for a missing user?) Or you're better off having an > "empty" value instead, such as an anonymous user object. That's still > type safe. >
I disagree here. Your user example with the anonymous user object works if I am asking for e.g. the creation of a session but it is a very different situation if I would be asking for a user profile. The fact that a user might have deleted her account is not an exceptional case, is is to be expected and should be handled accordingly. How is up to the caller. Otherwise any `array_key_exists($k, $a)` should result in an exception or some default value according to your logic here. I completely agree that `NULL` is not the best type and others languages have other means to solve the issue but absolutely every language has some way to indicate the absence of a meaningful value. PHP has `NULL` like many others and that is fine. The problem is not that we have `NULL` the problem is that it is just too often not checked if it can be `NULL`. Rust and many functional language solve this with an Option or Maybe that requires creation, allocation, and userland checks for every return and the compilers fail if you do not unpack it. Ceylon solves this with union types as we are discussing it right now, however, the difference to other languages is that the compiler will fail if you are not checking the return value and bugs are avoided in this way. This is something that we could adopt. http://ceylon-lang.org/documentation/faq/language-design/#optional_types The absence of something must be modellable. class ConstraintViolation { private ?Throwable $cause; private string $message; public function __construct( string $message, ?Throwable $cause = null ) { $this->message = $message; $this->cause = $cause; } public function getCause(): ?Throwable { return $this->cause; } public function hasCause(): bool { return isset($this->cause); } // ... } An example of a class I was just working on. How could, would, or should one model this without `NULL`? Throwing an exception? It is not exceptional for such an object and expected that there is no cause available. It is up to the caller how she wants to take care of this. /** @var ConstraintViolation $violation */ if ($violation->hasCause()) { // Handle cause ... } $cause = $violation->getCause(); if (isset($cause)) { // Handle cause ... } // Or maybe we just want it stringified? // Works nicely with `NULL`. :) echo $violation->getCause(); Throwing exceptions everywhere for non exceptional cases is a code smell too and I see that ever to often in current open source code. Especially throwing extremely low-level exception everywhere for real errors from which one should not recover (e.g. `InvalidArgumentException`) because they indicate developer mistakes and only add a lot of checks in production for no reason. That being said, the above could be rewritten to. class ConstraintViolation { public function getCause(): Throwable {} public function hasCause(): bool {} } This would mean that PHP emits an Error if someone calls `getCause` and there is no cause and there would be only one way to check: `hasCause`. This is a valid design decision since it minimizes the amount of possibilities to work with the class and avoids `NULL` altogether. However, the caller code did not really become less verbose; as was already illustrated in the above examples. Plus, the ability to directly use the `__toString`ability paired with `NULL` in a string is gone. Everything always has pros and cons and I do not think that there is one answer to all of these questions. Different languages handle this problem differently. PHP has `NULL` and we should keep it this way. To minimize bugs resulting from unchecked `NULL` usage a compiler feature could be implemented that warns a developer in such cases. We already have it for uncatched exceptions (although IDEs are currently not telling one about that; Eclipse does in Java though). -- Richard "Fleshgrinder" Fussenegger
signature.asc
Description: OpenPGP digital signature