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

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to