rfc:nullable_returns

PHP RFC: Nullable Return Type Declaration

Introduction

Functions that return Something or null are highly conventional in PHP software and libraries. PHP 7.0 return type declaration does not accommodate it. We believe this would be a valuable addition to PHP 7.1 while, at the same time, we think it's too soon to loosen PHP's brand new type features with Nullable Type declaration for function arguments or Union Types.

Relation to existing work

Union Types (v0.2 Feb 2015, Levi Morrison) proposes to allow function arguments and returns to be declared as any number of possible types, for example

function f(Foo|Bar|Baz $x): Blub|Bla|BlaBla {...}

Nullable Types (v0.2 Apr 2014, Levi Morrison) proposes a special subset of Union Types in which arguments and returns can be declared as Something or null. The Nullable Types RFC also proposes a shorthand grammar using ? while we use |null in order to be explicit. For example

function f(Foo|null $x): Blub|null {...}

Proposal

In this Nullable Return Type Declaration RFC we propose a special subset of the Nullable Types RFC proposal in which only the function return may be declared as nullable.

function f(): Blub|null {...}

PHP 7.0 already allows limited Nullable Type arguments via null default, e.g. function f(Foo $x = null) {} and we propose no change to that.

Note to the reader

The following arguments are based on opinion, experience and conversations. I tried to emphasize this by using first person singular when arguing my opinions and first person plural when referring to experience and opinion shared with my colleages. “We” specifically is not the journalistic “we” that refers to broader populations—it literally mean me and others I know and work with.

Arguments pro Nullable Returns

We have used object type hints for some time in PHP 5 and found the discipline they bring seems to improve code quality. Type hints help us think more carefully while programming. In code review and debugging they make it easier to understand the programmer's intent. So, in the expectation we'll make similar but even bigger profits with PHP 7's scalar type hints, return type declaration and strict types, we explored these features first.

Pretty much the first thing we noticed was that the return Something or null convention is incompatible with PHP 7.0 return type declarations.

In the return Something or null convention, the called method returns null when it cannot return a Something as requested but there's nothing unusual or unexpected about this outcome, i.e. the null does not signify an error. For example:

<?php
class Something
{
    public static findSomething(Criteria $criteria)
    {
        // Search persistent data for Something matching $criteria
 
        if (/* no match found */) {
            return null;
        }
 
        // return a matching Something
    }
}

It turns out this is a very common scenario and the return Something or null convention is widespread not just in our own code but in the PHP libraries we use.

As a workaround, can we find a way to represent that a returned instance of Something is in fact void of Something? The design would preferably be good enough to replace the current convention. Good enough must therefore involve being simple, obvious, and unambiguous, i.e. you couldn't possibly confuse a void Something with a real one. We haven't been able to devise anything remotely good enough.

In PHP 7.0 we found no better alternative for such methods than to omit a return declaration, which is rather sad.

Arguments pro Nullable Types and Union Types

Please refer to Nullable Types, Union Types.

Arguments contra Union Types

Unlike something or null returns, we haven't found a pressing need for Union Types in our work.

Unlike something or null returns, we generally don't have much difficulty finding workarounds. In fact, workaround isn't a good word for it because the reworked design that gets rid of Union function inputs and outputs often improves the code.

We want to use PHP strict type to enforce a discipline. API designs that allow a variety of types in a parameter, or that return a variety of types, are harder to understand for both the user and the implementer. The odds of making a mistake when handling Union Type inputs and outputs are worse than when the type is guaranteed to be foo. I prefer to seek an alternative design, even if it means rethinking part of the architecture. And in the unhappy case when we decide that Union is the least bad alternative, it seems fair to not declare return type at all and describe our failure in comments (with an apology).

PHP is “a pragmatic web-focused language” that “caters to the skill-levels of a wide range of users.” A Union Types new feature in PHP risks being interpreted as an encouragement to use it. I think this unwise—Union Types surely have their place but I believe they are also hazardous and programmers do well to avoid needing them. The quality of open source PHP libs could suffer if Union Types is advertised as a fine new addition to PHP.

Finally, now seems not the right time for Union Types in the arc of PHP's history. PHP neophytes learn early about its type juggling, how useful it is and, hopefully, also that it is hazardous. After many years we now have a new way to declare type in PHP with an option to be strict about it. This allows a radical tightening of the use of type in our programs. It seems to me that 7.1 is too soon for the radical loosening that Union Types represents.

So, while I see immediate need for Something or null returns, I don't believe generalizing this is warranted and could even turn out to be undesirable. Thus, according to my already mentioned gradualist preference, I think the PHP community should take time to use PHP 7's new type features before deciding that they should be dramatically loosened.

Arguments contra nullable argument type declarations

The arguments contra Union Types basically apply here too except that nullable argument declarations is a less radical loosening of PHP 7.0's new type declarations.

Unlike something or null returns, we haven't found a pressing need for nullable argument declarations. Existing PHP features, including a type with null default, has been sufficient for our needs. There has usually been a reasonable workaround in the design of the method signature. Indeed, sometimes the redesigned signature is better, suggesting that the discipline enforced by the current language limitations is worthwhile.

Proposal specifics

I propose PHP 7.1 introduce a grammar feature with which a function declares that it returns either a specified type or null. For example:

<?php
function foo(integer $i): Something|null
{
    return $i < 0 ? null : (new Something());
}

(I’m open to other grammars but I like this because of the correspondence with the familiar @return docblock style.)

Any function that is allowed to declare a return type may declare that it can also return null.

As in Nullable Types, the null marker can be removed by a subclass but it cannot be added.

Proposed PHP Version(s)

7.1.

RFC Impact

The proposal maintains backward compatibility, has no effect on SAPIs, and does not include new constants or php.ini configurations.

Future Scope

In due course, experience with PHP 7's new type hint and declaration features may show that the benefits of generalization to Nullable Type and potentially to Union Types outweigh the hazards. The author is not in favor of doing this soon.

Proposed Voting Choices

We propose this RFC for discussion as an option among others:

  1. Nullable return (this RFC)
  2. none of the above

We propose to see what transpires in the discussion period before proposing voting choices.

Implementation

I don't have the skills to do this myself. I failed to adapt https://github.com/php/php-src/pull/1045 although this may be a viable staring point. And I haven't yet found a volunteer to help.

rfc/nullable_returns.txt · Last modified: 2017/09/22 13:28 (external edit)