# [RFC] Nullable Types

- Thread: https://externals.io/message/92590
- Original archive: https://news-web.php.net/php.internals/92590
- Started by Lin Yo-An on 2016-04-21T11:00:06+00:00
- Messages: 8
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #92590 — Lin Yo-An — 2016-04-21T11:00:06+00:00

URL: https://externals.io/message/92590#92590

On Wed, Apr 20, 2016 at 11:05 PM, guilhermeblanco@gmail.com <
guilhermeblanco@gmail.com> wrote:
>
> Dmitry is even involved in the discussion of having IS_UNDEF until
> constructor ends, then enforcing type hinting at the end of constructor to
> trigger potential invalid instance state. It created a mess in the internal
> structure by creating a 3-state value: uninitialized, absent of value
> (null) and assigned value. All this problem would be solved by merging null
> into accepted value.
> So far the proposed solution there to take a wrong assumption to assume a
> default value based on type defined (like int = 0, string = '', etc), which
> are all potential valid values, leading to unpredictable behavior if a
> develop misses to assign a value to that property.
>

> Sure, people will say that now PHP will require a NullPointerException,
> PHP is turning into Java, or that I don't know what I'm talking about
> because they coded for Amiga and I don't (yes, I've seen that already in
> this mailing list). But the fact is that keeping control of 3-state flags
> are hard to maintain.
>

I think this is not to make PHP like Java, and it totally makes sense -
Nullable should be a type of a type instead of a state. In Haskell it's
named Maybe or Option, and It's better than NullPointerException.

Here is a discussion from Haskell community:
https://mail.haskell.org/pipermail/haskell-cafe/2011-April/091269.html

-- 
Best Regards,

Yo-An Lin

---

## #92606 — Fleshgrinder — 2016-04-21T19:52:39+00:00

URL: https://externals.io/message/92590#92606 · In reply to: #92590

> I think this is not to make PHP like Java, and it totally makes sense -
> Nullable should be a type of a type instead of a state. In Haskell it's
> named Maybe or Option, and It's better than NullPointerException.
> 
> Here is a discussion from Haskell community:
> https://mail.haskell.org/pipermail/haskell-cafe/2011-April/091269.html
> 

Why is it /better/?

final class None {}

final class Some {

    public $value;

    public function __construct($value) {
        $this->value = $value;
    }

}

final class Maybe {

    private static $none;

    private $value;

    private function __construct($value) {
        $this->value = $value;
    }

    public static function NOTHING() {
        if (self::$nothing === null) {
            self::$nothing = new Nothing();
        }
        return new static(self::$nothing);
    }

    public static function SOME($value) {
        return new static(new Some($value));
    }

    public function hasSome(): bool {
        return $this->value !== static::$none;
    }

    public function isNone(): bool {
        return $this->value === static::$none;
    }

    public function unwrap() {
        if ($this->value === static::$none) {
            trigger_error('NullPointerException', E_USER_ERROR);
        }
        return $this->value->value;
    }

}

// ----------------------------------------

function f1(): Option {}

$x = f1();
if ($x->hasSome()) {
    echo $x->unwrap(); // 42
}

$x = f1();
if ($x->isNone()) {
    echo -1;
}

echo f1()->unwrap(); // error: NullPointerException :P

// ----------------------------------------

function f2(): ?int {}

$y = f2();
if (isset($y)) {
    echo $y; // 42
}

$y = f2();
if ($y === null) {
    echo -1;
}

echo f2(); // null

You can easily build your own Option or Maybe and add the additional
bookkeeping to your runtime but it will not make your program more
secure or anything. It just adds more method calls and makes it more
verbose.

You can already use static code analyzers to detect if null is being
ignored in your code, however, right now they mainly have to rely on
PhpDoc that is often not properly documented. Adding an explicit syntax
to PHP for documentation would make PhpDoc obsolete and allow static
code analyzers to perform their job better and PHP to error out during
runtime if something else is being returned.

Honestly, null is not our problem. Every language has its null, just
because they wrap it or rename it does not make null suddenly vanish
from the universe. :P

-- 
Richard "Fleshgrinder" Fussenegger

---

## #92634 — Quim Calpe — 2016-04-22T09:42:34+00:00

URL: https://externals.io/message/92590#92634 · In reply to: #92606

> 
> > […]
>
> Why is it /better/?
>
> final class None {}
>
> final class Some {
>
>     public $value;
>
>     public function __construct($value) {
>         $this->value = $value;
>     }
>
> }
>
> final class Maybe {
>
>     private static $none;
>
>     private $value;
>
>     private function __construct($value) {
>         $this->value = $value;
>     }
>
>     public static function NOTHING() {
>         if (self::$nothing === null) {
>             self::$nothing = new Nothing();
>         }
>         return new static(self::$nothing);
>     }
>
>     public static function SOME($value) {
>         return new static(new Some($value));
>     }
>
>     public function hasSome(): bool {
>         return $this->value !== static::$none;
>     }
>
>     public function isNone(): bool {
>         return $this->value === static::$none;
>     }
>
>     public function unwrap() {
>         if ($this->value === static::$none) {
>             trigger_error('NullPointerException', E_USER_ERROR);
>         }
>         return $this->value->value;
>     }
>
> }
>
> // ----------------------------------------
>
> function f1(): Option {}
>
> $x = f1();
> if ($x->hasSome()) {
>     echo $x->unwrap(); // 42
> }
>
> $x = f1();
> if ($x->isNone()) {
>     echo -1;
> }
>
> echo f1()->unwrap(); // error: NullPointerException :P
>
> // ----------------------------------------
>
> function f2(): ?int {}
>
> $y = f2();
> if (isset($y)) {
>     echo $y; // 42
> }
>
> $y = f2();
> if ($y === null) {
>     echo -1;
> }
>
> echo f2(); // null
>
> You can easily build your own Option or Maybe and add the additional
> bookkeeping to your runtime but it will not make your program more
> secure or anything. It just adds more method calls and makes it more
> verbose.
>
> You can already use static code analyzers to detect if null is being
> ignored in your code, however, right now they mainly have to rely on
> PhpDoc that is often not properly documented. Adding an explicit syntax
> to PHP for documentation would make PhpDoc obsolete and allow static
> code analyzers to perform their job better and PHP to error out during
> runtime if something else is being returned.
>
> Honestly, null is not our problem. Every language has its null, just
> because they wrap it or rename it does not make null suddenly vanish
> from the universe. :P
>
> --
> Richard "Fleshgrinder" Fussenegger
>
>
IMHO, the point of Optional types is the intention, if you get an
Option<Foo> from a method, you have to deal with a None branch. Of course
you can just unwrap and go on, but it's a developer decision to do that,
not an oversight as using a Foo|null (or ?Foo) as an object directly.

---

## #92655 — Fleshgrinder — 2016-04-23T12:30:46+00:00

URL: https://externals.io/message/92590#92655 · In reply to: #92634

> IMHO, the point of Optional types is the intention, if you get an
> Option<Foo> from a method, you have to deal with a None branch. Of course
> you can just unwrap and go on, but it's a developer decision to do that,
> not an oversight as using a Foo|null (or ?Foo) as an object directly.
> 

IMOH, the point of ?T is the intention, if you get a null from a method,
you have to deal with a null branch. Of course you can just ignore it
and go on, but it's a developer decision to do that, not an oversight as
using a Option<T> as an object directly.

Sorry, but this works in both directions. The problem is not null, the
problem is that there is no system that warns you (e.g. a compiler)
about the missing null check. I think that Ceylon solved this problem
extremely nicely without the introduction of something special.

function fn(): ?T {}

$x = fn();
if (is $x T) {}
else {}

Not doing as above is a compiler error in Ceylon. However, I already
wrote multiple times that there are already statical code analysis tools
available that can find exactly such things in your code. One just needs
to use them.

-- 
Richard "Fleshgrinder" Fussenegger

---

## #92659 — Quim Calpe — 2016-04-23T16:40:55+00:00

URL: https://externals.io/message/92590#92659 · In reply to: #92655

Hi Richard,



> 
> > […]
>
> IMOH, the point of ?T is the intention, if you get a null from a method,
> you have to deal with a null branch. Of course you can just ignore it
> and go on, but it's a developer decision to do that, not an oversight as
> using a Option<T> as an object directly.
>
> Sorry, but this works in both directions. The problem is not null, the
> problem is that there is no system that warns you (e.g. a compiler)
> about the missing null check. I think that Ceylon solved this problem
> extremely nicely without the introduction of something special.
>

Of course, this works in both directions, but I see a value in Option types:

function getByEmail(string $email) : ?User {}
$user = getByEmail("foo@bar.com");
echo $user->getName();

I neglect the nullable return but works at first , all fine...
.. a week later... "Warning: Call to a member function getName() on null"

With Option type:
function getByEmail(string $email) : Option[User] {}
$user = getByEmail("foo@bar.com");
echo $user->getName();

IDE could warn me and It crashes right away, an option type must be
unwrapped so I get the "intention" immediately :)


>
> function fn(): ?T {}
>
> $x = fn();
> if (is $x T) {}
> else {}
>
> Not doing as above is a compiler error in Ceylon. However, I already
> wrote multiple times that there are already statical code analysis tools
> available that can find exactly such things in your code. One just needs
> to use them.
>

That's really nice

>
> --
> Richard "Fleshgrinder" Fussenegger
>
>
Option types are nice, but I feel we are going a bit off-topic. Option
types work better with other niceties like for comprehensions, pattern
matching... And I don't see PHP going that route in the near future, and
probably It's not okay for PHP to go that route...

Nullable return types is a better fit for PHP, null has been in the
language from the beginning, I agree here

---

## #92660 — Levi Morrison — 2016-04-23T16:58:32+00:00

URL: https://externals.io/message/92590#92660 · In reply to: #92659

> Hi Richard,
>
> 
>
> > […]
>
> Of course, this works in both directions, but I see a value in Option types:
>
> function getByEmail(string $email) : ?User {}
> $user = getByEmail("foo@bar.com");
> echo $user->getName();
>
> I neglect the nullable return but works at first , all fine...
> .. a week later... "Warning: Call to a member function getName() on null"
>
> With Option type:
> function getByEmail(string $email) : Option[User] {}
> $user = getByEmail("foo@bar.com");
> echo $user->getName();
>
> IDE could warn me and It crashes right away, an option type must be
> unwrapped so I get the "intention" immediately :)
>
>
> > […]
>
> That's really nice
>
> > […]
> Option types are nice, but I feel we are going a bit off-topic. Option
> types work better with other niceties like for comprehensions, pattern
> matching... And I don't see PHP going that route in the near future, and
> probably It's not okay for PHP to go that route...
>
> Nullable return types is a better fit for PHP, null has been in the
> language from the beginning, I agree here

Option is no better than a union type with null[1]. If a language
requires an option to be unwrapped then it can do the same with some
type or null. This is what Swift does. These things are exactly
equivalent.

However in PHP we do not have generics, which means a nullable type is
actually better because we can express the type that participates with
null. With an Option type we cannot.

  [1] At least with the behaviors here. If Option is a Traverable that
returns 0 or 1 items then we can use that behavior to our advantage.
Of course, we lose the ability to express the underlying option type.

---

## #92661 — Quim Calpe — 2016-04-23T17:14:03+00:00

URL: https://externals.io/message/92590#92661 · In reply to: #92660

> 
> > […]
> wrote:
> > […]
> course
> > […]
> that,
> > […]
> types:
> > […]
>
> Option is no better than a union type with null[1]. If a language
> requires an option to be unwrapped then it can do the same with some
> type or null. This is what Swift does. These things are exactly
> equivalent.
>

My point was about the developer receiving an Optional type, being less
error-prone than a nullable type. Not a language advantage.


>
> However in PHP we do not have generics, which means a nullable type is
> actually better because we can express the type that participates with
> null. With an Option type we cannot.
>

Yes, I was talking generically here, Option Types without generics-like
behaviour are a lot less useful
Nullable return types is indeed a better fit for PHP as I said

  [1] At least with the behaviors here. If Option is a Traverable that
> returns 0 or 1 items then we can use that behavior to our advantage.
> Of course, we lose the ability to express the underlying option type.

---

## #92706 — Jesse Schalken — 2016-04-25T06:18:52+00:00

URL: https://externals.io/message/92590#92706 · In reply to: #92660

> 
>
> Option is no better than a union type with null[1]. If a language
> requires an option to be unwrapped then it can do the same with some
> type or null. This is what Swift does. These things are exactly
> equivalent.
>

I don't think that's strictly true. Option<T> (or Maybe<T>) types that must
be unwrapped can usually be safely nested as Option<Option<...>>. This is
important if you have a generic class Foo<T> that uses Option<T> internally
and Foo is instantiated as Foo<Option<string>> (using string as an
example), causing an Option<Option<string>> to occur inside Foo. If
Option<string> meant string|null, the Option<Option<string>> would become
string|null|null and the real "null" at runtime would become ambiguous.

Nonetheless, nesting is very rarely needed, but it is something to keep in
mind when using generics in combination with union with null for
nullability.



> However in PHP we do not have generics, which means a nullable type is
> actually better because we can express the type that participates with
> null. With an Option type we cannot.
>
>   [1] At least with the behaviors here. If Option is a Traverable that
> returns 0 or 1 items then we can use that behavior to our advantage.
> Of course, we lose the ability to express the underlying option type.
