rfc:intersection_types

PHP RFC: Intersection Types

Introduction

Sometimes multiple behaviors are necessary for a routine to be able to do its job but there is no common interface for those behaviors. Sometimes a common interface can be extracted, but this is not doable in third-party code. This RFC proposes a way to define types that must satisfy multiple types.

Proposal

Intersection types allow the programmer to write multiple type declarations for a parameter or return value. The value must satisfy all of the declared types. Each type is separated by ampersand (&).

Here is a practical example. Given this definition:

function RecordsToList(Countable & Traversable $input): String {
    if (count($input) > 0) {
        $output = "<ol>\n";
        foreach ($input as $value) {
            $output .= "\t<li>" . htmlentities($value) . "</li>\n";
        }
        $output .= "</ol>\n";
        return $output;
    } else {
        return "<p>No items to display.</p>\n";
    }
}

This will error (see on 3v4l.org):

// Argument 1 passed to RecordsToList() must be Countable and Traversable, array given
echo RecordsToList(["Lennon", "McCartney", "Starr", "Harrison"]);

This works correctly (see on 3v4l):

echo RecordsToList(new ArrayObject(["Lennon", "McCartney", "Starr", "Harrison"]));
// Output:
// <ol>
//     <li>Lennon</li>
//     <li>McCartney</li>
//     <li>Starr</li>
//     <li>Harrison</li>
// </ol>

Note that if Union Types are also accepted then something like this will be possible1), allowing for an array or an object that satisfies both Countable and Traversable:

function RecordsToList(Array | (Countable & Traversable) $input): String {
    if (count($input) > 0) {
        $output = "<ol>\n";
        foreach ($input as $value) {
            $output .= "\t<li>" . htmlentities($value) . "</li>\n";
        }
        $output .= "</ol>\n";
        return $output;
    } else {
        return "<p>No items to display.</p>\n";
    }
}

When union and intersection types are in the same expression they must be grouped with parenthesis (demonstrated above). The following is invalid:

function RecordsToList(Array | Countable & Traversable $input): String {
    // ...
}

Backward Incompatible Changes

There are no known backwards compatibility breaks.

Proposed PHP Version(s)

This feature is proposed for PHP 7.1.

Open Issues

Make sure there are no open issues when the vote starts!

Proposed Voting Choices

This feature will have a simple Yes/No vote requiring two-thirds in the affirmative.

Patches and Tests

A proof of concept patch has been provided by Joe Watkins and Bob Weinand: https://github.com/php/php-src/pull/1887

Note there are multiple features contained in this patch, such as union types.

References

1)
The experimental patch provided by Joe and Bob currently does not support mixing unions and intersections
rfc/intersection_types.txt · Last modified: 2017/09/22 13:28 (external edit)