Honour the Default Value of 'return' Statement According to the Function Signature

php.internals

Unnamed Person

5 years ago
Hello Internals, I congratulate you for the successful release of PHP 8.0.3. I also appreciate your efforts to bring union types and return type hints. Although the type hints have made debugging more easier, it has created another problem for those that use 'return' to end the execution of a function or method call. There are mainly two usages of 'return'. First ends the execution of the remaining statements. Second returns a value. The return type hints aids with the second usage and ignores the former facility. In other words, if one wants to avoid large code blocks i.e. the conditional blocks, it can write like following. <?php function foo() { If (true /*condition1*/) return; if (!false /*condition2*/) return; /*the remaining part of a function*/ }; ?> The above function returns null if conditions are met. Otherwise, it returns no value. It was okay when there was no concept of return type hints. When return types have been introduced, the behaviour needs to be evolved, too. What if the return type was set 'int|float' in the above example? The compiler would give following error: Fatal error: A function with return type must return a value.... Similarly, a function with ': void' signature produceed that a function may not return a value. To resolve such a problem, I request to honour the function signature when you evaluate return statements. Instead of looking for no 'return' statements in ': void' functions, verify whether a function specifies a return value. If the function returns a type other than the types declared in the signature, it may produce wrong type error. If a function with void returns a value i.e. contains a non-empty return (return;) statement, the compiler may throw an error stating that a function with void signature must not return a value. Likewise, 'return' may return an empty array if the return type was set to array; the similar case applies to string, int, float, object etcetera. In the case of ': void' signature, 'return' only ends the execution and returns no value. PHP also allows to specify only returning 'false' from a function call. What if you extend this functionality and allow specifying literals including null? If nulls are also allowed, the backword behaviour could be impleamented. For example: public function foo() : string| null; Consider the following example for literals. <?php function get_nationality_string(int $country_code) : string | "unknown" { return; }; ?> I am positive that you will consider my request and provide me with the feedback in case of any inconsistency in my argument. I wish you best of luck for the on-going work for the PHP 8.1. Best Regards Hamza Ahmad

Rowan Collins

5 years ago
On 11 March 2021 03:37:52 GMT, office.hamzaahmad@gmail.com wrote:
><?php >function get_nationality_string(int $country_code) : string | "unknown" >{ > return; >};
If I understand you correctly, your idea is that the "return;" here would be treated automatically as "return 'unknown';" I think that's an interesting idea, although I can't immediately think of a situation where I'd use it. My main concerns are with the syntax: - Firstly, specific literals aren't currently allowed in return types, and allowing them would have other implications. Three exception is "false", which is allowed because of its common use as an error code, including in many internal functions. - Secondly, it could be ambiguous which is intended to be the default value, if the return type was something like int|"yes"|"no" Perhaps the default return value would need to be specified separately from the return type somehow? Regards, Hi Hamza, Welcome to the list, and thanks for sharing this idea.
-- Rowan Tommins [IMSoP]

Unnamed Person

5 years ago
Hi Rowan, Thanks for the response. Luckily, you razed the question that I thought about after posting the first request. My idea develops on respecting both return type hints and union types. The current implementation of empty return does not respect the function signature. As I have mentioned in the first message that empty return returns null regardless of the return type set, I propose that an empty return may return the defaults of types i.e. an empty string if the type is string, an empty array if the type is array, zero if the type is int etcetera. I have also noted that in a void typed function, an empty return may return nothing and just end the execution. The current implementation of empty return is only favourable for mixed type. In other words, when the return type is mixed, an empty return may return null. Otherwise, it should respect the declared return type. After the release of union types, there emerges a question regarding the selection of default return value. To solve this problem, the type on the very right side will be used. Now, I come to your question, Rowan. As of today, only false and null literals are supported. If both assist in error handling, why does empty return always returns null? I tested this code on shell. var_dump($a=(function():string|false{return;})()); It produced: Fatal error: A function with return type must return a value in php shell code on line 1. Here comes the inconsistency. To resolve the problem, I suggest to respect the literals first. If literals are not found, respect the first type on the right side. Moreover, only two standalone literals, false and null, will be supported. Otherwise, the literal should be one of the types specified. If multiple literals are specified, it will throw an error of multiple literals. The literal can be a simple literal, such as, "unknown" 0, and a constant expression, like self :: MSG_UNKNOWN. It will help maintain the multi-language scripts. I hope I have been able to make my stance clear. Best Hamza Ahmad -----Original Message----- From: Rowan Tommins <rowan.collins@gmail.com> Sent: Thursday, March 11, 2021 1:45 PM To: internals@lists.php.net Subject: Re: [PHP-DEV] Honour the Default Value of 'return' Statement According to the Function Signature On 11 March 2021 03:37:52 GMT, office.hamzaahmad@gmail.com wrote:
><?php >function get_nationality_string(int $country_code) : string | "unknown" >{ > return; >};
If I understand you correctly, your idea is that the "return;" here would be treated automatically as "return 'unknown';" I think that's an interesting idea, although I can't immediately think of a situation where I'd use it. My main concerns are with the syntax: - Firstly, specific literals aren't currently allowed in return types, and allowing them would have other implications. Three exception is "false", which is allowed because of its common use as an error code, including in many internal functions. - Secondly, it could be ambiguous which is intended to be the default value, if the return type was something like int|"yes"|"no" Perhaps the default return value would need to be specified separately from the return type somehow? Regards, Hi Hamza, Welcome to the list, and thanks for sharing this idea.
-- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php

Victor Bolshov

5 years ago
IMO this feature would contradict with the Single Responsibility Principle. Type hints are for type hinting, not for value hinting. Cheers. Sent from Mailspring (https://link.getmailspring.com/link/4D67851F-DE69-45D9-9DB1-54B16845E340@getmailspring.com/0?redirect=https%3A%2F%2Fgetmailspring.com%2F&recipient=aW50ZXJuYWxzQGxpc3RzLnBocC5uZXQ%3D), the best free email app for work On Mar 11 2021, at 10:51 am, Hamza Ahmad <office.hamzaahmad@gmail.com> wrote:

Unnamed Person

5 years ago
Hi Victor, It does not contradict because it does not cover two different aspects. First, return types specify the types of to be returned values. Second, this request proposes a shorthand technique to avoid writing a single line code multiple times. Third, it is similar to default argument values that are also typed. Cheers! From: Victor Bolshov <crocodile2u@gmail.com> Sent: Thursday, March 11, 2021 3:00 PM To: Hamza Ahmad <office.hamzaahmad@gmail.com> Cc: Rowan Tommins <rowan.collins@gmail.com>; internals@lists.php.net Subject: Re: [PHP-DEV] Honour the Default Value of 'return' Statement According to the Function Signature IMO this feature would contradict with the Single Responsibility Principle. Type hints are for type hinting, not for value hinting. Cheers. Sent from Mailspring <https://link.getmailspring.com/link/4D67851F-DE69-45D9-9DB1-54B16845E340@getmailspring.com/0?redirect=https%3A%2F%2Fgetmailspring.com%2F&recipient=b2ZmaWNlLmhhbXphYWhtYWRAZ21haWwuY29t> , the best free email app for work On Mar 11 2021, at 10:51 am, Hamza Ahmad <office.hamzaahmad@gmail.com> wrote: Hi Rowan, Thanks for the response. Luckily, you razed the question that I thought about after posting the first request. My idea develops on respecting both return type hints and union types. The current implementation of empty return does not respect the function signature. As I have mentioned in the first message that empty return returns null regardless of the return type set, I propose that an empty return may return the defaults of types i.e. an empty string if the type is string, an empty array if the type is array, zero if the type is int etcetera. I have also noted that in a void typed function, an empty return may return nothing and just end the execution. The current implementation of empty return is only favourable for mixed type. In other words, when the return type is mixed, an empty return may return null. Otherwise, it should respect the declared return type. After the release of union types, there emerges a question regarding the selection of default return value. To solve this problem, the type on the very right side will be used. Now, I come to your question, Rowan. As of today, only false and null literals are supported. If both assist in error handling, why does empty return always returns null? I tested this code on shell. var_dump($a=(function():string|false{return;})()); It produced: Fatal error: A function with return type must return a value in php shell code on line 1. Here comes the inconsistency. To resolve the problem, I suggest to respect the literals first. If literals are not found, respect the first type on the right side. Moreover, only two standalone literals, false and null, will be supported. Otherwise, the literal should be one of the types specified. If multiple literals are specified, it will throw an error of multiple literals. The literal can be a simple literal, such as, "unknown" 0, and a constant expression, like self :: MSG_UNKNOWN. It will help maintain the multi-language scripts. I hope I have been able to make my stance clear. Best Hamza Ahmad -----Original Message----- From: Rowan Tommins <rowan.collins@gmail.com> Sent: Thursday, March 11, 2021 1:45 PM To: internals@lists.php.net Subject: Re: [PHP-DEV] Honour the Default Value of 'return' Statement According to the Function Signature On 11 March 2021 03:37:52 GMT, office.hamzaahmad@gmail.com wrote:
><?php
>function get_nationality_string(int $country_code) : string | "unknown"
>{
> return;
>};
If I understand you correctly, your idea is that the "return;" here would be treated automatically as "return 'unknown';" I think that's an interesting idea, although I can't immediately think of a situation where I'd use it. My main concerns are with the syntax: - Firstly, specific literals aren't currently allowed in return types, and allowing them would have other implications. Three exception is "false", which is allowed because of its common use as an error code, including in many internal functions. - Secondly, it could be ambiguous which is intended to be the default value, if the return type was something like int|"yes"|"no" Perhaps the default return value would need to be specified separately from the return type somehow? Regards, Hi Hamza, Welcome to the list, and thanks for sharing this idea.
-- Rowan Tommins [IMSoP] -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: https://www.php.net/unsub.php

Larry Garfield

5 years ago
On Thu, Mar 11, 2021, at 6:10 AM, Hamza Ahmad wrote:
> Hi Victor, > > It does not contradict because it does not cover two different aspects. > First, return types specify the types of to be returned values. Second, > this request proposes a shorthand technique to avoid writing a single > line code multiple times. Third, it is similar to default argument > values that are also typed. > > Cheers!
Greetings. First, please don't top post. This is a bottom-posting list. As to your proposal, it's a bit hard to follow because it sounds like you're suggesting an implicit "return of nothing" built on union types, when based on your follow ups what I think you're suggesting is something more like this: function foo(int $bar = 4): int = 0 { if (is_even($bar)) { return; // Implicitly returns 0 } return $bar * 2; } That is, allowing a default return value. Union types aren't part of the picture. I can see how you got to the idea, but honestly I am not a fan. Primarily... I've never run into the situation you describe where multiple early-returns would have the same value. Perhaps it's just because in those cases I end up combining the conditionals with an ||, but I don't see the problem you describe. Different code paths should result in different results, or else why are they different code paths? So, code that's doing that I'd be suspicious of to begin with. Even if you did end up in such a situation, since you're dealing with a static value, setting the default internally within the function is trivial: function foo(int $bar = 4): int { $default = 0; if (is_even($bar)) { return $default; } return $bar * 2; } So basically, in the few cases where this isn't a code smell, it's unnecessary. --Larry Garfield

Chase Peeler

5 years ago
On Thu, Mar 11, 2021 at 10:37 AM Larry Garfield <larry@garfieldtech.com> wrote:
> On Thu, Mar 11, 2021, at 6:10 AM, Hamza Ahmad wrote: > > Hi Victor, > > > > It does not contradict because it does not cover two different aspects. > > First, return types specify the types of to be returned values. Second, > > this request proposes a shorthand technique to avoid writing a single > > line code multiple times. Third, it is similar to default argument > > values that are also typed. > > > > Cheers! > > Greetings. First, please don't top post. This is a bottom-posting list. > > As to your proposal, it's a bit hard to follow because it sounds like > you're suggesting an implicit "return of nothing" built on union types, > when based on your follow ups what I think you're suggesting is something > more like this: > > function foo(int $bar = 4): int = 0 { > if (is_even($bar)) { > return; // Implicitly returns 0 > } > return $bar * 2; > } > > That is, allowing a default return value. Union types aren't part of the > picture. > > I can see how you got to the idea, but honestly I am not a fan. > Primarily... I've never run into the situation you describe where multiple > early-returns would have the same value. Perhaps it's just because in > those cases I end up combining the conditionals with an ||, but I don't see > the problem you describe. Different code paths should result in different > results, or else why are they different code paths? > > So, code that's doing that I'd be suspicious of to begin with. > >
There are times, for the sake of readability, where I might break things up into multiple if statements instead of combining them with an OR. Sometimes you need to do additional work between conditionals as well: if($id == 0){ return null; } $record = $db->get($id); if(empty($record)){ return null; } Regardless, I think your solution below works just fine.
> Even if you did end up in such a situation, since you're dealing with a > static value, setting the default internally within the function is trivial: > > function foo(int $bar = 4): int { > $default = 0; > if (is_even($bar)) { > return $default; > } > return $bar * 2; > } > > So basically, in the few cases where this isn't a code smell, it's > unnecessary. > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > >
-- Chase Peeler chasepeeler@gmail.com

Mike Schinkel

5 years ago
> On Mar 10, 2021, at 10:37 PM, office.hamzaahmad@gmail.com wrote: > > Hello Internals, > I congratulate you for the successful release of PHP 8.0.3. I also > appreciate your efforts to bring union types and return type hints. > Although the type hints have made debugging more easier, it has created > another problem for those that use 'return' to end the execution of a > function or method call.
Welcome.
> There are mainly two usages of 'return'. First ends the execution of the > remaining statements. Second returns a value. The return type hints aids > with the second usage and ignores the former facility. In other words, if > one wants to avoid large code blocks i.e. the conditional blocks, it can > write like following. > > <?php > function foo() > { > If (true /*condition1*/) > return; > > if (!false /*condition2*/) > return; > > /*the remaining part of a function*/ > }; > ?>
However, I agree with Larry. This feels either like a code smell and/or could be handled with a variable. This pattern can be very frustrating in other people's code when I either need to debug it or might want to debug it to learn the code because it requires setting IDE breakpoints in many places to be sure that catch an exit. And invariably I miss breakpointing a return when debugging and am left wondering what happened. See https://youtrack.jetbrains.com/issue/WI-40862 As an alternate I developed a technique using `do{...} while(...)` in a novel way that I wish PHP supported more cleanly, but it nonetheless works great. The technique uses `do {...} while(false)` to create a block that break can exit early on guard clauses, for example (btw, I would return `null` instead of `'unknown'` and enclose the breaks in braces for the `if(...)` statement, but I wanted my example to be equivalent to yours): <?php function foo() { $value = 'unknown'; // Set the default return value up here do { if (/*condition1*/) break; // <== see, no need for a value here if (/*condition2*/) break; // <== or here! /*the remaining part of a function that sets $value */ } while (false); return $value; // The only return in this function. Debugging nirvana. } Maybe you can use this approach instead to allow you to not have to annotate each early exit with a return value? -Mike P.S. Ideally PHP would support (something like) a naked `do{...}` — or a naked `try{...}` that supports `break` — though I have not tried to convince the list because status quo has the inertia here. But I mention this in hopes others see value in it and would help champion the idea. #fwiw

Unnamed Person

5 years ago
Thank you for providing me with the feedback, experts! Initially, I wanted empty returns in void functions. When I prepared the request, I included the other solutions that I felt important and worth adding. Is it not possible to go with empty returns in void functions? I agree that the code can be adjusted to fit the current environment; still, people avoid writing lengthy blocks of code. An empty return with defaults will help them with the code that is easier to maintain. I clarified my stance on first email in a response to a respectable member of the list. I do not know why that email is not appearing on externals. I will make sure that next time I post an external URL that contains the changelog and do not clutter the mailing list. I am again thankful to you all! Best Hamza Ahmad -----Original Message----- From: Mike Schinkel <mike@newclarity.net> Sent: Thursday, March 11, 2021 10:05 PM To: office.hamzaahmad@gmail.com Cc: internals@lists.php.net Subject: Re: [PHP-DEV] Honour the Default Value of 'return' Statement According to the Function Signature
> On Mar 10, 2021, at 10:37 PM, office.hamzaahmad@gmail.com wrote: > > Hello Internals, > I congratulate you for the successful release of PHP 8.0.3. I also > appreciate your efforts to bring union types and return type hints. > Although the type hints have made debugging more easier, it has > created another problem for those that use 'return' to end the > execution of a function or method call.
Welcome.
> There are mainly two usages of 'return'. First ends the execution of > the remaining statements. Second returns a value. The return type > hints aids with the second usage and ignores the former facility. In > other words, if one wants to avoid large code blocks i.e. the > conditional blocks, it can write like following. > > <?php > function foo() > { > If (true /*condition1*/) > return; > > if (!false /*condition2*/) > return; > > /*the remaining part of a function*/ > }; > ?>
However, I agree with Larry. This feels either like a code smell and/or could be handled with a variable. This pattern can be very frustrating in other people's code when I either need to debug it or might want to debug it to learn the code because it requires setting IDE breakpoints in many places to be sure that catch an exit. And invariably I miss breakpointing a return when debugging and am left wondering what happened. See https://youtrack.jetbrains.com/issue/WI-40862 As an alternate I developed a technique using `do{...} while(...)` in a novel way that I wish PHP supported more cleanly, but it nonetheless works great. The technique uses `do {...} while(false)` to create a block that break can exit early on guard clauses, for example (btw, I would return `null` instead of `'unknown'` and enclose the breaks in braces for the `if(...)` statement, but I wanted my example to be equivalent to yours): <?php function foo() { $value = 'unknown'; // Set the default return value up here do { if (/*condition1*/) break; // <== see, no need for a value here if (/*condition2*/) break; // <== or here! /*the remaining part of a function that sets $value */ } while (false); return $value; // The only return in this function. Debugging nirvana. } Maybe you can use this approach instead to allow you to not have to annotate each early exit with a return value? -Mike P.S. Ideally PHP would support (something like) a naked `do{...}` — or a naked `try{...}` that supports `break` — though I have not tried to convince the list because status quo has the inertia here. But I mention this in hopes others see value in it and would help champion the idea. #fwiw