Re: Improving PHP's type system
| From: | Stephen Coakley | Date: | Wed, 13 Apr 2016 20:12:10 +0000 |
| Subject: | Re: Improving PHP's type system | ||
| References: | 1 2 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-92263@lists.php.net to get a copy of this message | ||
On Wed, 13 Apr 2016 10:53:13 -0600, Levi Morrison wrote:
> Continued (hit send by hotkey accident):
>
> On Wed, Apr 13, 2016 at 10:50 AM, Levi Morrison <levim@php.net> wrote:
>> First, some background: several versions of PHP ago authors of
>> functions and methods could only restrict types to the array type or a
>> class/interface type. We slowly added some other types such as callable
>> and primitive types. These tools have been invaluable to those who care
>> about restricting the types of their inputs and outputs. This type
>> information reduces the code each author has to write if they want to
>> restrict to working with certain types and it also provides a form of
>> documentation. Overall these features have been well received and are
>> considered a good thing to do.
>>
>> However, as we have added these new restrictions we still cannot
>> express exactly what types are permitted in some common cases.
>>
>> 1. Return types cannot specify the function will return only type T
>> or Null.
>> 2. Parameter types cannot express that an iterable type is required.
>> It's common for functions to work a stream of data and it is irrelevant
>> if it is an array or a Traversable object.
>> 3. Parameter types cannot express that that the parameter must
>> implement two different interfaces. For example, requiring a parameter
>> to implement both Countable and Traversable cannot be done.
>>
>> There are some common work-arounds to these issues:
>>
>> - Omit the type information and rely on documentation.
>> - Check the parameter type inside the function (eg
>>
assert(is_array($x) || $x instanceof Traversable))
>> - Introduce a new type that embodies the restrictions. If this in an
>> interface it must be implemented by every object you hope to use the
>> restriction with.
>>
>> In some cases these work-arounds are tolerable. However, some of them
>> are really painful.
>>
>> - Requiring a new supertype is intrusive. Code cannot always be
>> changed to use the new supertype. For example:
>> - Upstream projects that would not benefit from your changes.
>> - Primitives cannot be extended except by altering the engine.
>> In these cases a new type has to be introduced that proxies
>> behavior to the underlying object/primitive.
>> - Relying on documentation can lead to subtle and hard to diagnose
>> issues when it is used incorrectly. Erroring immediately is sometimes
>> preferable.
>>
>> All of these issues I've outlined can be nicely resolved by adding two
>> new kinds of types to our system: union and intersection types. A union
>> type requires the variable to match at least one of the types. An
>> intersection type requires the variable to match all of the types. The
>> vertical par symbol (OR) is used for unions and ampersand (AND) is used
>> for intersections. For example:
>>
>> function (A | B $var); would require $var to be either type A or
>> type B.
>> function (A & B $var); would require $var to be type A and type B.
>>
>> To accommodate the common use-case of returning some type or Null we
>> would need to formally allow Null as an explicit type:
>>
>> function (): T | Null;
>>
>> Since this is a common use case some languages have a short-hand
>> notation to represent a union with Null:
>
> function (): ?T;
>
> Examples of such languages include Swift, C#, OCaml and Hack (though the
> symbol is sometimes in the front and sometimes in the back.
>
> Later today I will be submitting RFCs for each of these proposals.
> Although they can work independently it is helpful to understand the
> overall goal and how they fit together, which has been the purpose of
> this email. Feedback for each RFC will belong in the thread for that
> piece, but discussion about the ideas overall is better suited here.
COOLNESS!! +10
I really like this mindset of a general solution to these stricter types.
I think it will present a more consistent "interface" for using types
across PHP in the future.
In general, this is a more general and elegant solution to nullable type
in properties, hints, and return types.
I am eager to see what you have in those RFCs. 😃
--
Stephen