Re: [RFC] Nullable Types

php.internals

Lin Yo-An

10 years ago
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

Fleshgrinder

10 years ago
On 4/21/2016 1:00 PM, Lin Yo-An wrote:
> 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

Quim Calpe

10 years ago
On Thu, Apr 21, 2016 at 9:52 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> On 4/21/2016 1:00 PM, Lin Yo-An wrote: > > 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 > >
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.

Fleshgrinder

10 years ago
On 4/22/2016 11:42 AM, Quim Calpe wrote:
> 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

Quim Calpe

10 years ago
Hi Richard, On Sat, Apr 23, 2016 at 2:30 PM, Fleshgrinder <php@fleshgrinder.com> wrote:
> On 4/22/2016 11:42 AM, Quim Calpe wrote: > > 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. >
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

Levi Morrison

10 years ago
On Sat, Apr 23, 2016 at 10:40 AM, Quim Calpe <quim@kalpe.com> wrote:
> Hi Richard, > > On Sat, Apr 23, 2016 at 2:30 PM, Fleshgrinder <php@fleshgrinder.com> wrote: > >> On 4/22/2016 11:42 AM, Quim Calpe wrote: >> > 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. >> > > 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
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.

Quim Calpe

10 years ago
On Sat, Apr 23, 2016 at 6:58 PM, Levi Morrison <levim@php.net> wrote:
> On Sat, Apr 23, 2016 at 10:40 AM, Quim Calpe <quim@kalpe.com> wrote: > > Hi Richard, > > > > On Sat, Apr 23, 2016 at 2:30 PM, Fleshgrinder <php@fleshgrinder.com> > wrote: > > > >> On 4/22/2016 11:42 AM, Quim Calpe wrote: > >> > 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. > >> > > > > 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 > > 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

Jesse Schalken

10 years ago
On Sun, Apr 24, 2016 at 2:58 AM, Levi Morrison <levim@php.net> wrote:
> On Sat, Apr 23, 2016 at 10:40 AM, Quim Calpe <quim@kalpe.com> wrote: > > 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.