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 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.
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
I can appreciate that you want only the restricted union with null.
However, I do not see the point of disallowing it for parameter typesMy 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
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
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 forfoo(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.
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
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
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 putis_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).
Unless you like having
is_null()
scattered around your code in a hundredplaces... 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 thatfoo(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
Unless you like having
is_null()
scattered around your code in a hundredplaces... 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 thatfoo(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
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.
convinced by the rebuttles to change my mind.
s/rebuttles/rebuttals/
I thought about just letting it go like many typos/spelling mistakes
but it seemed too funny to not correct
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
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 hasNULL
and we should keep it this way. To
minimize bugs resulting from uncheckedNULL
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).
Rather than debate the relative merits of Null as a concept, since I
think all agree removing Null from the language is not even a thing,
what do you think of my recommendation to NOT have the ? shorthand for
"or null" on types, but keep the |null for union types? That is, if you
want to return Foo or null, you have to use the long-hand version:
function foo($a) : Foo|null
That
- Allows for explicit or-null behavior.
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage. - Neatly avoids the ?Foo / Foo? question entirely.
I think that's a win-win arrangement.
--Larry Garfield
Hi!
2016-04-17 23:56 GMT-04:00 Larry Garfield larry@garfieldtech.com:
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 hasNULL
and we should keep it this way. To
minimize bugs resulting from uncheckedNULL
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).Rather than debate the relative merits of Null as a concept, since I think
all agree removing Null from the language is not even a thing, what do you
think of my recommendation to NOT have the ? shorthand for "or null" on
types, but keep the |null for union types? That is, if you want to return
Foo or null, you have to use the long-hand version:function foo($a) : Foo|null
That
- Allows for explicit or-null behavior.
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage.- Neatly avoids the ?Foo / Foo? question entirely.
I think that's a win-win arrangement.
--Larry Garfield
--
I can agree with that.
Language design wise, if you have no way to forecast how language features
independently proposed are going to interact then it's a good idea to have
a converging strategy. Fortunately, this seems to be happening almost
spontaneously towards union types representation, as in the "multiple
catch" RFC (https://wiki.php.net/rfc/multiple-catch) now in voting phase.
Even if we decide not to commit to a full union types implementation now,
the design process will be much easier in the future if we decide to use
Foo|null instead of Foo? or ?Foo and we will not end up with 2 or 3 ways
alternative to declare the same thing.
Cheers,
Márcio
2016-04-17 23:56 GMT-04:00 Larry Garfield larry@garfieldtech.com:
Rather than debate the relative merits of Null as a concept, since I think
all agree removing Null from the language is not even a thing, what do you
think of my recommendation to NOT have the ? shorthand for "or null" on
types, but keep the |null for union types? That is, if you want to return
Foo or null, you have to use the long-hand version:function foo($a) : Foo|null
That
- Allows for explicit or-null behavior.
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage.- Neatly avoids the ?Foo / Foo? question entirely.
I think that's a win-win arrangement.
I can agree with that.
Language design wise, if you have no way to forecast how language features
independently proposed are going to interact then it's a good idea to have
a converging strategy. Fortunately, this seems to be happening almost
spontaneously towards union types representation, as in the "multiple
catch" RFC (https://wiki.php.net/rfc/multiple-catch) now in voting phase.Even if we decide not to commit to a full union types implementation now,
the design process will be much easier in the future if we decide to use
Foo|null instead of Foo? or ?Foo and we will not end up with 2 or 3 ways
alternative to declare the same thing.
I actually do not see a problem with the shorthand notation. I am
usually an extreme opponent of duplicated or aliased functionality
because it increases the cognitive load to keep all this crap in your
mind. This is however a useful shortcut and shortcuts are good things in
terms of usability if done write. Think of <Ctrl> + <c> vs. right click
copy (or any other example of useful keyboard shortcuts). The usage of
null as an union type is the most common case compared to all the
others that are truly rare in comparison.
There are other shortcuts in PHP that are perfect:
- Iterator vs. Generator: they can achieve the exact same thing but
slightly different and a generator can almost always be used to
replace an iterator with less code, even if the special effects of
it are irrelevant. -
$x = $x + $y
vs.$x += $y
: we have many of those and that is just
fine as it is. -
$x = $x + 1
vs.++$x
vs.$x++
: ditto -
<?php echo 'Hello World'
vs.<?= 'Hello World'
: ditto - if-else vs.
?:
: ditto - public function vs. function in interfaces: I think the sole reason
why people include the public access modifier is because no PHP IDE
automatically adds the public while implementing the methods. - while vs. do-while vs. for vs. foreach: It is possible to achieve
everything with a while loop. - ...
Plain and simple, shorthand functionality is not always and with no
exceptions bad. On the contrary, it can make your life a whole lot
easier (I think this is best illustrated by the three loop constructs
which are all super usable).
That being said, I can also live with leaving out the shorthand
notation. Simply because we currently have to write it out fully in
PhpDoc anyways (not that this ever discouraged anyone to misuse null
everywhere, even if they properly documented everything).
Last but not least, 3) is already answered for me. It must be prefix to
allow later extensions like generics. Plus HHVM already has it as a
prefix which is another good argument in my book.
--
Richard "Fleshgrinder" Fussenegger
This is however a useful shortcut and shortcuts are good things in
terms of usability if done write. Think of <Ctrl> + <c> vs. right clickcopy (or any other example of useful keyboard shortcuts).
Except when some sod adds a <shift> or decides left click is quicker ...
all of which are in use on one or other Linux desktop tool :(
A CONSISTENT way of doing things would save a lot of time, so adding
even deeper levels of functionality is just overkill ...
--
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
This is however a useful shortcut and shortcuts are good things in
terms of usability if done write. Think of <Ctrl> + <c> vs. right clickcopy (or any other example of useful keyboard shortcuts).
Except when some sod adds a <shift> or decides left click is quicker ...
all of which are in use on one or other Linux desktop tool :(A CONSISTENT way of doing things would save a lot of time, so adding
even deeper levels of functionality is just overkill ...
Everything that is good can backfire if overused. We need to ensure that
we do not do that and the fact that we are discussing it right here
shows that we are not taking the decision lightly. :)
--
Richard "Fleshgrinder" Fussenegger
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage.
This implies that using 'null' is wrong ... and is a slippery slope to
then 'disabling' null where some people think it should be removed. If
PHP development is making these sort of decisions it should be clear
that is the intention, not just 'hopefully discourages' things that some
people have no appreciation of the usefulness of!
--
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
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage.
This implies that using 'null' is wrong ... and is a slippery slope to
then 'disabling' null where some people think it should be removed. If
PHP development is making these sort of decisions it should be clear
that is the intention, not just 'hopefully discourages' things that some
people have no appreciation of the usefulness of!
I am not sure what you're implying by "some people have no appreciation
of the usefulness of"... I am well aware of NULL's use cases. I am also
well aware that the general zeitgeist in the language development
community is that they're dangerous; the designer of the first language
to have them calls them a "billion dollar mistake", many languages
actively avoid having NULL
in favor of something deliberately more
structured. NULLs are a very common cause of avoidable fatal errors in
many languages (including PHP). NULLs are rude to users of your API, as
it balloons the error handling code they need to deal with.
I am on record and will continue to be on record that null is usually
wrong. I consider that position entirely justified, as does the
academic CS community. I am also now on record suggesting that we use
union types to allow type-or-null returns/parameters, which you're
welcome to quote me on. :-)
Are you comfortable with the compromise position I've proposed?
--
--Larry Garfield
- Doesn't make it quite so easy, which hopefully discourages (but does
not prohibit) its usage.
This implies that using 'null' is wrong ... and is a slippery slope to
then 'disabling' null where some people think it should be removed. If
PHP development is making these sort of decisions it should be clear
that is the intention, not just 'hopefully discourages' things that some
people have no appreciation of the usefulness of!I am not sure what you're implying by "some people have no appreciation
of the usefulness of"... I am well aware of NULL's use cases. I am also
well aware that the general zeitgeist in the language development
community is that they're dangerous; the designer of the first language
to have them calls them a "billion dollar mistake", many languages
actively avoid havingNULL
in favor of something deliberately more
structured. NULLs are a very common cause of avoidable fatal errors in
many languages (including PHP). NULLs are rude to users of your API, as
it balloons the error handling code they need to deal with.
With a properly structured code flow, NULL
is the ONLY logical solution
to elements of the data that do not physically exist. And in my book
that includes where we run out of whole records when running that work
flow. PHP is a scripting language that works well with these flows of
data, and NULL
is certainly NOT a mistake IN that workflow. So in order
to remove NULL
you need to come up with a reason why the SQL standard is
wrong and produce a new specification that can replace it ... and
'exception' is just another mistake that needs eliminating where that is
not appropriate when a simple NULL
currently does the job more than
adequately.
I am on record and will continue to be on record that null is usually
wrong. I consider that position entirely justified, as does the
academic CS community. I am also now on record suggesting that we use
union types to allow type-or-null returns/parameters, which you're
welcome to quote me on. :-)Are you comfortable with the compromise position I've proposed?
As long as I can simply wipe any use of ANY of this type code overload
from my libraries and keep it out of ANY of the code PHP functionality
it so that the database extensions still handle 'null' exactly as it is
designed to work then that is all I need. I don't see ANY need to add
internally the very code you are complaining we need to add externally
removing the simple flexibility that provides. So I don't see the need
for any compromise ... just scrap the whole waste of time adding
unnecessary code and documenting why you think PHP is currently so
broken ...
--
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
Hi!
I am not sure what you're implying by "some people have no appreciation
of the usefulness of"... I am well aware of NULL's use cases. I am also
well aware that the general zeitgeist in the language development
community is that they're dangerous; the designer of the first language
I would like to note in general that following the latest fashion in
academic development is not always a good idea for PHP. It's fine when
you experiment with academic languages, but when you have language that
a) focused on simplicity and low entrance barrier and b) is in
production use by millions and has 20 years of existing practices,
libraries and habits, we have to be a bit more wary, I think. I am not
saying we should not improve, or ignore academic developments, I am
saying that we should be careful with not jumping to the idea-of-the-day
bandwagon too fast, before it is clear it is good and necessary for PHP.
As an example, I don't see how exactly eliminating null would be good
for PHP, or doing anything with null for that matter.
to have them calls them a "billion dollar mistake", many languages
actively avoid havingNULL
in favor of something deliberately more
structured. NULLs are a very common cause of avoidable fatal errors in
many languages (including PHP). NULLs are rude to users of your API, as
it balloons the error handling code they need to deal with.
I think this description is misleading in a way. Nulls are immediate
causes of many errors, technically, in a meaning that "if I find null
where I expected to have DB connection, I get an error" - but this is
only a technical cause. The real cause is the failure to check whether
your attempt to connect to the database succeeded, or that you have
proper DB configuration, or such, and null is just a symptom of that
error - and that error is not automagically fixed by having some other
object instead of null. You have to actually design and write code for
it, there's no replacement for it.
So declaring null "cause of all evils" is ignoring the real causes and
blaming immediate technical cause instead, and it would not be helpful.
It is true that for some languages with more complex type systems it is
possible to largely get rid of nulls by using those complex types and
ensure type system controls for all checks at least to be present (note
that doesn't guarantee proper function either, since no type system can
ensure you handle the case of missing DB connection properly, it just
can ensure you have a branch for it). But that requires much more
complex and involved type system that PHP has - or, in my opinion,
should have.
I am on record and will continue to be on record that null is usually
wrong. I consider that position entirely justified, as does the
academic CS community. I am also now on record suggesting that we use
union types to allow type-or-null returns/parameters, which you're
welcome to quote me on. :-)
I think this position is way too narrow, simplistic and detached from
real language use towards abstract academic purity. There are tons of
causes where using null is completely fine and exactly what is needed. I
also think union types is a great and unnecessary complication of the
language and in order to do them properly we'd need lots of facilities
PHP does not have (like type pattern matching and static compilation)
and I am not sure it should have - ones looking for Haskell know where
to find it.
And there are a lot of cases where failing on null is exactly what one
needs - if it doesn't work, just fail and let somebody come and check
what is going on. If your database died, your DB-bound code is not going
to fix it. With or without nulls - it has to just fail and wait for
somebody to fix it. There's no need to overcomplicate that.
Stas Malyshev
smalyshev@gmail.com
to have them calls them a "billion dollar mistake", many languages
actively avoid havingNULL
in favor of something deliberately more
structured. NULLs are a very common cause of avoidable fatal errors in
many languages (including PHP). NULLs are rude to users of your API, as
it balloons the error handling code they need to deal with.I think this description is misleading in a way. Nulls are immediate
causes of many errors, technically, in a meaning that "if I find null
where I expected to have DB connection, I get an error" - but this is
only a technical cause. The real cause is the failure to check whether
your attempt to connect to the database succeeded, or that you have
proper DB configuration, or such, and null is just a symptom of that
error - and that error is not automagically fixed by having some other
object instead of null. You have to actually design and write code for
it, there's no replacement for it.So declaring null "cause of all evils" is ignoring the real causes and
blaming immediate technical cause instead, and it would not be helpful.
It is true that for some languages with more complex type systems it is
possible to largely get rid of nulls by using those complex types and
ensure type system controls for all checks at least to be present (note
that doesn't guarantee proper function either, since no type system can
ensure you handle the case of missing DB connection properly, it just
can ensure you have a branch for it). But that requires much more
complex and involved type system that PHP has - or, in my opinion,
should have.
I agree with Stanislav here. NULL is not evil per se and just because
some functional languages get some attention right now does not mean
that they solved the biggest mistake in CS history. On the contrary,
they just invented another way of handling it by introducing new types
that require more book keeping and by refusing to compile if they
encounter a place where proper handling is missing.
As I wrote earlier, we could introduce such a feature into the language.
However, static code analyzers can already provide this functionality.
People just need to use them.
TL;DR NULL is just fine if used correctly and we cannot make sure that
people use it correctly.
--
Richard "Fleshgrinder" Fussenegger
If your database died, your DB-bound code is not going
to fix it. With or without nulls - it has to just fail and wait for
somebody to fix it. There's no need to overcomplicate that.
If the DB read failed due to the connection being lost, then that is a
case for an exception and out of flow events? The 'null' would be
returned when an active connection has no data to return because a
search or query has produced none. Larry would probably be looking to
check the 'no_of_recs' to see if there were any results but then you
still have to read the result if there is one ... overcomplicating the
simple solution?
( ADOdb check the connection is still valid as part of the query prep
and run and changes the work flow if required )
--
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
I would like to note in general that following the latest fashion in
academic development is not always a good idea for PHP. It's fine when
you experiment with academic languages, but when you have language that
a) focused on simplicity and low entrance barrier and b) is in
production use by millions and has 20 years of existing practices,
libraries and habits, we have to be a bit more wary, I think. I am not
saying we should not improve, or ignore academic developments, I am
saying that we should be careful with not jumping to the idea-of-the-day
bandwagon too fast, before it is clear it is good and necessary for PHP.
...
I agree with Stas, not just this paragraph but pretty all of the email.
Within the context of today's PHP language, practices, and the tried and
true libraries and frameworks we rely on, I don't know how, for certain
necessary semantics, to improve on Something or null return contracts.
I all honesty, I've looked for alternatives that satisfy a specific type
but I can't find anything better.
For example, I use the active record ORM from the Yii 2 framework. My
User model class therefore extends ActiveRecord. If I search for a User
record matching given criteria (e.g. an email address) the parent class
needs a generic way represent that no matching record exists and that
this is not an exception.
The convention in PHP is to return null in this situation. I have tried
to imagine how the search method might return an instance of the User
model class that represents "not a user record", i.e. the absence of any
user matching the search criteria. PHP's version of OOP seems not to
have a intrinsic feature for a function to return an object with type
Something but void of any Something object instance.
If PHP has nothing to model this, what convention can we invent as a
workaround to encode "empty" in an actual instance of ActiveRecord or
any subtype? A magic property can conflict with the app's model
attributes. But an ActiveRecord::isEmpty() method could work. This
satisfies the desire to return a specific subtype of ActiveRecord (e.g.
User) but it also introduces hazardous complexity: what do you do with
an instance in which the model's attributes, including primary key, are
valid but isEmpty() returns true, or vise versa.
Moreover, in what way is this better than returning PHP null? What have
we gained with this isEmpty() conventions?
Tom
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
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 putis_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