[RFC] Nullable Return Type Declaration

php.internals

Tom Worster

10 years ago
I would like to introduce for discussion an RFC proposing and arguing for Nullable Return Type Declaration in 7.1 and deferring for now more general relaxations of 7.0 type as proposed in Levi's two RFCs. https://wiki.php.net/rfc/nullable_returns If anyone would like to collaborate on the RFC, I have a repo you may fork: https://gist.github.com/tom--/e95a10fbe4d34f8a72c9 (although guthub's formatting isn't lovely). I'm looking for help with implementation. Tom

Levi Morrison

10 years ago
On Thu, Apr 14, 2016 at 9:39 AM, Tom Worster <fsb@thefsb.org> wrote:
> I would like to introduce for discussion an RFC proposing and arguing for > Nullable Return Type Declaration in 7.1 and deferring for now more general > relaxations of 7.0 type as proposed in Levi's two RFCs. > > https://wiki.php.net/rfc/nullable_returns > > If anyone would like to collaborate on the RFC, I have a repo you may fork: > https://gist.github.com/tom--/e95a10fbe4d34f8a72c9 (although guthub's > formatting isn't lovely). I'm looking for help with implementation. > > Tom > > >
I can appreciate that you want only the restricted union with null. However, I do not see the point of disallowing it for parameter types while allowing it for return types: function setLeft(Node $n = null) { $this->left = $n; $this->updateHeight(); } Why disallow the explicit union with null here instead of the default parameter which does not exactly capture the desired semantics? Calling `$node->setLeft()` is just odd, almost as if it was a mistake. I would much prefer `$node->setLeft(null)` here. Basically, if we have a feature for return types that exactly matches the semantics that we occasionally want for the parameter types why forbid it? Additionally, on occasion I'll see functions like this: function foo(Bar $b = null, $not_optional_param); Why not allow nullable types on parameters to avoid that wonkiness caused by default values of null? function foo(Bar | Null $b, $not_optional_param); This is much better.

Fleshgrinder

10 years ago
On 4/14/2016 6:35 PM, Levi Morrison wrote:
> On Thu, Apr 14, 2016 at 9:39 AM, Tom Worster <fsb@thefsb.org> wrote: >> I would like to introduce for discussion an RFC proposing and arguing for >> Nullable Return Type Declaration in 7.1 and deferring for now more general >> relaxations of 7.0 type as proposed in Levi's two RFCs. >> >> https://wiki.php.net/rfc/nullable_returns >> >> If anyone would like to collaborate on the RFC, I have a repo you may fork: >> https://gist.github.com/tom--/e95a10fbe4d34f8a72c9 (although guthub's >> formatting isn't lovely). I'm looking for help with implementation. >> >> Tom >> >> >> > > I can appreciate that you want only the restricted union with null. > However, I do not see the point of disallowing it for parameter types > while allowing it for return types: > > function setLeft(Node $n = null) { > $this->left = $n; > $this->updateHeight(); > } > > Why disallow the explicit union with null here instead of the default > parameter which does not exactly capture the desired semantics? > Calling `$node->setLeft()` is just odd, almost as if it was a mistake. > I would much prefer `$node->setLeft(null)` here. Basically, if we have > a feature for return types that exactly matches the semantics that we > occasionally want for the parameter types why forbid it? > > Additionally, on occasion I'll see functions like this: > > function foo(Bar $b = null, $not_optional_param); > > Why not allow nullable types on parameters to avoid that wonkiness > caused by default values of null? > > function foo(Bar | Null $b, $not_optional_param); > > This is much better. >
My guess is that this RFC only wants to get it for return because it might be an easier vote? I do not like the fact that only the long version of union types is supported and prefer the question mark approach as a short hand. Can this RFC be either extended to include that as well or removed so it does not get in the way of the other two RFCs?
-- Richard "Fleshgrinder" Fussenegger

Tom Worster

10 years ago
On 4/14/16, 1:33 PM, "Fleshgrinder" <php@fleshgrinder.com> wrote:
>On 4/14/2016 6:35 PM, Levi Morrison wrote: >>I can appreciate that you want only the restricted union with null. >> However, I do not see the point of disallowing it for parameter types >> >My guess is that this RFC only wants to get it for return because it >might be an easier vote?
Hi Richard, That wasn't really my intent. I tried to set out my argument contra nullable param type in the RFC and elaborate it in my answer to Levi, which I hope you read. My attitude to programming reversed since 10 years ago. I used to prefer to have all the options and be allowed to exercise my judgement. But over those years I had to remain responsible for most of my code, which led to a blinding conversion. Now I am so acutely aware of how likely I am to write bugs that I more often than not want the language to get smaller. My sense is that nullable params won't turn out to be one of the good parts, in the Crockford sense. Something|null return, otoh, is so established as a convention I can't imagine getting away from it. I'm aware that some people won't understand my point of view. If that's still the case, ask again and I'll try a different answer. Tom

Tom Worster

10 years ago
On 4/14/16, 12:35 PM, "Levi Morrison" <morrison.levi@gmail.com on behalf of levim@php.net> wrote:
>I can appreciate that you want only the restricted union with null. >However, I do not see the point of disallowing it for parameter types >while allowing it for return types: > > function setLeft(Node $n = null) { > $this->left = $n; > $this->updateHeight(); > } > >Why disallow the explicit union with null here instead of the default >parameter which does not exactly capture the desired semantics? >Calling `$node->setLeft()` is just odd, almost as if it was a mistake. >I would much prefer `$node->setLeft(null)` here. Basically, if we have >a feature for return types that exactly matches the semantics that we >occasionally want for the parameter types why forbid it?
I agree that `$node->setLeft()` is weird but I find `$node->setLeft(null)` still a bit weird. Perhaps something like `$node->resetLeft()` would work? Was that the idea?
>Additionally, on occasion I'll see functions like this: > > function foo(Bar $b = null, $not_optional_param);
The only thing we can know for sure from this is that the programmer urgently needs reeducation :)
>Why not allow nullable types on parameters to avoid that wonkiness >caused by default values of null? > > function foo(Bar | Null $b, $not_optional_param); > >This is much better.
Yes but still a code smell to me. I'd need to know more about the programmer's intent for `foo(null, "value")`. It might be better to swap order, or change the method name, or add another method... Who knows? Need to take each case individually. This kind of asking questions about intent in code review is good for code quality. That's why I like how PHP doesn't allow this. It encourages the question asking. Every case is different, of course, so you can surely find counter examples. But on balance I'd say it's better to disallow it. Does this help you understand my preference? I think the restriction encourages a healthy discipline. Otoh, I think nullable return is a pressing need. Tom

Levi Morrison

10 years ago
>>Why not allow nullable types on parameters to avoid that wonkiness >>caused by default values of null? >> >> function foo(Bar | Null $b, $not_optional_param); >> >>This is much better. > > Yes but still a code smell to me. I'd need to know more about the > programmer's intent for `foo(null, "value")`. It might be better to swap > order, or change the method name, or add another method... Who knows? Need > to take each case individually.
Having a separate method instead of `foo(null, "value")` makes it difficult to use for the result of a function. Assume `bar()` returns `Bar | Null`; this would no longer work if they were separated: foo(bar(), "value") Functions are often composed so if something is the output of one function it will likely be the input to another. Thus if returning optionally null values is important to you so should optionally null parameters.

Tom Worster

10 years ago
On 4/14/16, 5:46 PM, "Levi Morrison" <morrison.levi@gmail.com on behalf of levim@php.net> wrote:
>Having a separate method instead of `foo(null, "value")` makes it >difficult to use for the result of a function.
I suspect that might be a good thing:) I don't know for sure but the possibility exists.
>Assume `bar()` returns >`Bar | Null`; this would no longer work if they were separated: > > foo(bar(), "value") > >Functions are often composed so if something is the output of one >function it will likely be the input to another. Thus if returning >optionally null values is important to you so should optionally null >parameters.
This was a chin-scratcher for me. On one hand, I see what you mean. On the other I couldn't think of an example from my experience (which I admit is very narrow -- I live a sheltered life) of such a bar() that I would feed straight foo(). The semantic convention for Something or null return, as I see it, is when bar() returns null, it is saying "I got nothing". What kind of foo() does the same thing to nothing at all as it does to a Something object? This is where I got stuck. Say I was doing the composition instead via chaining. Something::bar()->foo("value") is nonsense if bar() could return null. This suggests to me that the other composition might not be wise. *Either* bar() should not be returning Something or null (maybe it should instead return some other type that can represent all the possible returns) *or* we shouldn't try to compose like this and should test for the Somethingness of bar()'s return before apply ->foo("value") or foo(…, "value") to it. Or maybe this API needs an even more fundamental redesign. So, from my perspective, this might be an example of the limitation nudging us to think harder about the design. Tom

Larry Garfield

10 years ago
On 04/14/2016 07:33 PM, Tom Worster wrote:
> On 4/14/16, 5:46 PM, "Levi Morrison" <morrison.levi@gmail.com on behalf of > levim@php.net> wrote: > >> Having a separate method instead of `foo(null, "value")` makes it >> difficult to use for the result of a function. > I suspect that might be a good thing:) I don't know for sure but the > possibility exists. > > >> Assume `bar()` returns >> `Bar | Null`; this would no longer work if they were separated: >> >> foo(bar(), "value") >> >> Functions are often composed so if something is the output of one >> function it will likely be the input to another. Thus if returning >> optionally null values is important to you so should optionally null >> parameters. > This was a chin-scratcher for me. On one hand, I see what you mean. On the > other I couldn't think of an example from my experience (which I admit is > very narrow -- I live a sheltered life) of such a bar() that I would feed > straight foo(). > > The semantic convention for Something or null return, as I see it, is when > bar() returns null, it is saying "I got nothing". What kind of foo() does > the same thing to nothing at all as it does to a Something object? This is > where I got stuck. > > Say I was doing the composition instead via chaining. > Something::bar()->foo("value") is nonsense if bar() could return null. > This suggests to me that the other composition might not be wise. > > *Either* bar() should not be returning Something or null (maybe it should > instead return some other type that can represent all the possible > returns) *or* we shouldn't try to compose like this and should test for > the Somethingness of bar()'s return before apply ->foo("value") or foo(…, > "value") to it. Or maybe this API needs an even more fundamental redesign. > > So, from my perspective, this might be an example of the limitation > nudging us to think harder about the design. > > Tom
I have to agree with Tom. if foo(bar(), $val) could die because bar() may return NULL, that's not an indication that foo() needs to accept NULL. It's a sign that bar() should not be returning it in the first place. :-) I am highly, highly sceptical about nullable parameters or returns, and frankly would rather they were not included in the language. By nature they undermine type safety. At best, they indicate to all callers "*every* time you call this function, you MUST put is_null() around it or your program may fail randomly." While that's better to know explicitly than not (which is the case for any untyped return, aka any PHP code pre-7.0), it would be better still to, well, not have to worry about that billion dollar mistake[1] cropping up in my code. In a sense, if we really must allow for value-or-null (which I consider a code smell in the 98% case) I'd prefer if it was ONLY available via union types: That is, Something|null. That's longer and clumsier to type, and harder to read. Which it should be. (Static casts in C++ have a fugly syntax, which has been defended by the language designers on the grounds that static casts are fugly, so the syntax for them should be as well to remind you to stop doing it. There is a fair amount of validity to that argument on affordance grounds, at least within C++.) Using an easy short hand notation for something that is inherently a code smell when you're already typing your code only serves to encourage something we should be training people out of in the first place. Unless you like having is_null() scattered around your code in a hundred places... I don't. :-) [1] https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions --Larry Garfield

Levi Morrison

10 years ago
On Thu, Apr 14, 2016 at 6:48 PM, Larry Garfield <larry@garfieldtech.com> wrote:
> On 04/14/2016 07:33 PM, Tom Worster wrote: >> >> On 4/14/16, 5:46 PM, "Levi Morrison" <morrison.levi@gmail.com on behalf of >> levim@php.net> wrote: >> >>> Having a separate method instead of `foo(null, "value")` makes it >>> difficult to use for the result of a function. >> >> I suspect that might be a good thing:) I don't know for sure but the >> possibility exists. >> >> >>> Assume `bar()` returns >>> `Bar | Null`; this would no longer work if they were separated: >>> >>> foo(bar(), "value") >>> >>> Functions are often composed so if something is the output of one >>> function it will likely be the input to another. Thus if returning >>> optionally null values is important to you so should optionally null >>> parameters. >> >> This was a chin-scratcher for me. On one hand, I see what you mean. On the >> other I couldn't think of an example from my experience (which I admit is >> very narrow -- I live a sheltered life) of such a bar() that I would feed >> straight foo(). >> >> The semantic convention for Something or null return, as I see it, is when >> bar() returns null, it is saying "I got nothing". What kind of foo() does >> the same thing to nothing at all as it does to a Something object? This is >> where I got stuck. >> >> Say I was doing the composition instead via chaining. >> Something::bar()->foo("value") is nonsense if bar() could return null. >> This suggests to me that the other composition might not be wise. >> >> *Either* bar() should not be returning Something or null (maybe it should >> instead return some other type that can represent all the possible >> returns) *or* we shouldn't try to compose like this and should test for >> the Somethingness of bar()'s return before apply ->foo("value") or foo(…, >> "value") to it. Or maybe this API needs an even more fundamental redesign. >> >> So, from my perspective, this might be an example of the limitation >> nudging us to think harder about the design. >> >> Tom > > > I have to agree with Tom. if foo(bar(), $val) could die because bar() may > return NULL, that's not an indication that foo() needs to accept NULL. It's > a sign that bar() should not be returning it in the first place. :-) > > I am highly, highly sceptical about nullable parameters or returns, and > frankly would rather they were not included in the language. By nature they > undermine type safety. At best, they indicate to all callers "*every* time > you call this function, you MUST put is_null() around it or your program may > fail randomly." While that's better to know explicitly than not (which is > the case for any untyped return, aka any PHP code pre-7.0), it would be > better still to, well, not have to worry about that billion dollar > mistake[1] cropping up in my code. > > In a sense, if we really must allow for value-or-null (which I consider a > code smell in the 98% case) I'd prefer if it was ONLY available via union > types: That is, Something|null. That's longer and clumsier to type, and > harder to read. Which it should be. (Static casts in C++ have a fugly > syntax, which has been defended by the language designers on the grounds > that static casts are fugly, so the syntax for them should be as well to > remind you to stop doing it. There is a fair amount of validity to that > argument on affordance grounds, at least within C++.) Using an easy short > hand notation for something that is inherently a code smell when you're > already typing your code only serves to encourage something we should be > training people out of in the first place. > > Unless you like having is_null() scattered around your code in a hundred > places... I don't. :-) > > [1] https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions > > --Larry Garfield
My point is that `foo(bar(), $val)` won't die because bar may return null. Bar is expected to return null sometimes. For example, let's consider an administrator page where they look up user information based on an identifier. The routine we'll use will have this signature: function get_user(string $id): User | Null; It is possible for an identifier to not exist and this is not an error (database successfully returned no results). If there is no User data to display then it makes sense for the UI to present that differently. Thus it makes sense to pass that User | Null onto the code that will present it: $user_data = get_user($id); // ... $user_html = render_user_data($user_data); In fact this is a common operation that is encountered in many code bases (I think every single one I've ever looked at).

Lester Caine

10 years ago
On 15/04/16 05:22, Levi Morrison wrote:
>> Unless you like having is_null() scattered around your code in a hundred >> > places... I don't. :-)
You have lots of code instead in exception handling away fro the normal program flow?
>> > [1] https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions >> > >> > --Larry Garfield > My point is that `foo(bar(), $val)` won't die because bar may return > null. Bar is expected to return null sometimes. > > For example, let's consider an administrator page where they look up > user information based on an identifier. The routine we'll use will > have this signature: > > function get_user(string $id): User | Null; > > It is possible for an identifier to not exist and this is not an error > (database successfully returned no results). If there is no User data > to display then it makes sense for the UI to present that differently. > Thus it makes sense to pass that User | Null onto the code that will > present it: > > $user_data = get_user($id); > // ... > $user_html = render_user_data($user_data); > > In fact this is a common operation that is encountered in many code > bases (I think every single one I've ever looked at).
That is a possible database type scenario, although on all of my systems, the 'guest' user will be accessed as a default which gives the default user data set. The main thing I see with the 'null is not needed' argument is that it instead relies on 'exception handling'? If I am scanning a file or reading a record set, at some point I hit the end, and in ALL my code base I get a null object rather than result object, be that reading and processing a file or a database feed. We have already had the complaints about file handling should give an exception when there is nothing left, but MY workflow keeps everything in line ... when the last record is processed we see the 'null' and progress to the next step in the process. There is nothing here that needs to involve throwing exceptions which may well be coded out of line with the main program flow and make debugging more difficult?
-- Lester Caine - G8HFL ----------------------------- Contact - http://lsces.co.uk/wiki/?page=contact L.S.Caine Electronic Services - http://lsces.co.uk EnquirySolve - http://enquirysolve.com/ Model Engineers Digital Workshop - http://medw.co.uk Rainbow Digital Media - http://rainbowdigitalmedia.co.uk

Larry Garfield

10 years ago
On 4/15/16 4:55 AM, Lester Caine wrote:
> On 15/04/16 05:22, Levi Morrison wrote: >>> Unless you like having is_null() scattered around your code in a hundred >>>> places... I don't. :-) > You have lots of code instead in exception handling away fro the normal > program flow? > >>>> [1] https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions >>>> >>>> --Larry Garfield >> My point is that `foo(bar(), $val)` won't die because bar may return >> null. Bar is expected to return null sometimes. >> >> For example, let's consider an administrator page where they look up >> user information based on an identifier. The routine we'll use will >> have this signature: >> >> function get_user(string $id): User | Null; >> >> It is possible for an identifier to not exist and this is not an error >> (database successfully returned no results). If there is no User data >> to display then it makes sense for the UI to present that differently. >> Thus it makes sense to pass that User | Null onto the code that will >> present it: >> >> $user_data = get_user($id); >> // ... >> $user_html = render_user_data($user_data); >> >> In fact this is a common operation that is encountered in many code >> bases (I think every single one I've ever looked at). > That is a possible database type scenario, although on all of my > systems, the 'guest' user will be accessed as a default which gives the > default user data set. > > The main thing I see with the 'null is not needed' argument is that it > instead relies on 'exception handling'? If I am scanning a file or > reading a record set, at some point I hit the end, and in ALL my code > base I get a null object rather than result object, be that reading and > processing a file or a database feed. We have already had the complaints > about file handling should give an exception when there is nothing left, > but MY workflow keeps everything in line ... when the last record is > processed we see the 'null' and progress to the next step in the > process. There is nothing here that needs to involve throwing exceptions > which may well be coded out of line with the main program flow and make > debugging more difficult?
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'm not suggesting that we purge NULL from the language. That's impractical/impossible. I'm suggesting we shouldn't soften the type system added in 7.0, which discourages its use in most cases, as it should be.
-- --Larry Garfield

Levi Morrison

10 years ago
> I'm not suggesting that we purge NULL from the language. That's > impractical/impossible. I'm suggesting we shouldn't soften the type system > added in 7.0, which discourages its use in most cases, as it should be.
It is a common misconception I've heard several times now that union types is weakening or softening the type system. I want to reiterate that it is already possible to use multiple types. It is already done in the wild. It is done all over our standard library. The union types RFC just allows you to encode the specific types to be passed or returned. In other words it is aiming to make using the dynamic types of PHP safer by allowing you to explicitly write out the options. The Nullable Return Types RFC is basically arguing that the only useful union of types is `T | Null` and only for return types. I've already stated a few of my concerns with that belief and have not been convinced by the rebuttles to change my mind. Also, to specifically address a concern Larry had earlier about nullable types in general:
> I am highly, highly sceptical about nullable parameters or returns, and frankly would rather they were not included in the language.
Just to clarify: we already have nullable parameters because of a default of null. It's already in the language and people have asked many times for this functionality to be expanded to include return types. I should have kept a master list every time someone contacted me on github, email, twitter, etc about this so I could easily list them all as references in the relevant RFCs for demand for the feature.

Fleshgrinder

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

Tom Worster

10 years ago
On 4/15/16 12:22 AM, Levi Morrison wrote:
> My point is that `foo(bar(), $val)` won't die because bar may return > null. Bar is expected to return null sometimes. > > For example, let's consider an administrator page where they look up > user information based on an identifier. The routine we'll use will > have this signature: > > function get_user(string $id): User | Null; > > It is possible for an identifier to not exist and this is not an error > (database successfully returned no results). If there is no User data > to display then it makes sense for the UI to present that differently. > Thus it makes sense to pass that User | Null onto the code that will > present it: > > $user_data = get_user($id); > // ... > $user_html = render_user_data($user_data); > > In fact this is a common operation that is encountered in many code > bases (I think every single one I've ever looked at).
This is a good example. My opinion is that *because* get_user() can return null (a red flag) I prefer to see explicit handing of the null case before doing anything else. If I would end up with `render_user_data(get_user($id))` I would consider it fair to not hint the param because I didn't earn it. I invented the faux docblock tag @sorry for this kind of thing. Tom

Tom Worster

10 years ago
On 4/14/16 8:48 PM, Larry Garfield wrote:
> I am highly, highly sceptical about nullable parameters or returns, and > frankly would rather they were not included in the language. By nature > they undermine type safety. At best, they indicate to all callers > "*every* time you call this function, you MUST put is_null() around it > or your program may fail randomly."
Yes.
> While that's better to know > explicitly than not (which is the case for any untyped return, aka any > PHP code pre-7.0), it would be better still to, well, not have to worry > about that billion dollar mistake[1] cropping up in my code.
I agree. To be clear, I do not intend the RFC to encourage nullable return or suggest that it's a fine thing to use. But given where we are, it's hard to imagine how to extirpate it. When we started using PHP 7.0 type, initially when authoring new models (and using Yii2), it immediately became clear that we lacked two things: this and void returns. We're getting the latter in 7.1. It would be very nice if we could have both. I'm a practical PHP user, with a generally conservative attitude to the language, often unmoved by proposals add a feature because some other more fashionable language has it (I call it language envy, to borrow from Freud). And while PHP 7.0 is good, I'd rather have Something|null in the return declaration than just in the docblock. That's all.
> In a sense, if we really must allow for value-or-null (which I consider > a code smell in the 98% case) I'd prefer if it was ONLY available via > union types: That is, Something|null. That's longer and clumsier to > type, and harder to read. Which it should be. (Static casts in C++ have > a fugly syntax, which has been defended by the language designers on the > grounds that static casts are fugly, so the syntax for them should be as > well to remind you to stop doing it. There is a fair amount of validity > to that argument on affordance grounds, at least within C++.) Using an > easy short hand notation for something that is inherently a code smell > when you're already typing your code only serves to encourage something > we should be training people out of in the first place.
With regard to syntax, I prefer the long form `Something|null`. That seems very clear to me. The proposed short-hand ? syntax always makes me think of what I hate most about regex. Tom