rfc:generics

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
rfc:generics [2016/03/20 20:16]
mindplay add link to tests
rfc:generics [2018/06/02 17:33]
mindplay add github link
Line 6: Line 6:
   * Status: Draft   * Status: Draft
   * First Published at: http://wiki.php.net/rfc/generics   * First Published at: http://wiki.php.net/rfc/generics
 +
 +**NOTE:** a newer version of this RFC may be under development [[https://github.com/mindplay-dk/php-generics-rfc|on GitHub]].
  
 ===== Introduction ===== ===== Introduction =====
  
-This RCF proposes the addition of generic types and functions to PHP.+This RFC proposes the addition of generic types and functions to PHP.
  
 Generics enable developers to create a whole family of declarations using a single generic declaration - for example, a generic collection-type declaration ''Collection<T>'' induces a declaration for any entity-type ''T'', which negates the need to implement a dedicated collection-type for every entity-type, reducing the need for boilerplate code and duplication. Generics enable developers to create a whole family of declarations using a single generic declaration - for example, a generic collection-type declaration ''Collection<T>'' induces a declaration for any entity-type ''T'', which negates the need to implement a dedicated collection-type for every entity-type, reducing the need for boilerplate code and duplication.
Line 117: Line 119:
  
 In the second example, the ''ValueType'' is incorrect, which results in a ''TypeError''. In the second example, the ''ValueType'' is incorrect, which results in a ''TypeError''.
 +
 +=== Nested Type Arguments ===
 +
 +Generic classes may be instantiated and generic functions/methods may be called with nested type arguments.
 +
 +<code php>
 +class Container<ContentType>
 +{
 +    private $content;
 +    
 +    public function getContent(): ContentType
 +    {
 +        return $this->content;
 +    }
 +    
 +    public function setContent(ContentType $content): void
 +    {
 +        $this->content = $content;
 +    }
 +}
 +
 +$container = new Container<Entry<int,string>>();
 +
 +$container->setContent(new Entry<int,string>(1, 'test'));
 +var_dump($container->getContent() instanceof Entry<int,string>); // => (bool) true
 +
 +$container->setContent(new Entry<int,int>(1, 1)); // throws a TypeError
 +</code>
 +
 +In this example, the ''ContentType'' has retained the nested type arguments within ''Entry<KeyType, ValueType>'' The responsibility of type checking arguments to ''Entry'' still belong to the ''Entry'' class, yet the type hierarchy is maintained all the way up to the root definition ''Container<Entry<int,string>>''.
 +
 +In the second example, the ''ValueType'' is incorrect just as before, which again results in a ''TypeError''.
  
 === Upper Bounds === === Upper Bounds ===
Line 146: Line 180:
 Any valid PHP type-hint may be used as an upper bound, including simple types like ''int'', ''float'', ''bool'', ''string'' and ''object''. (Omission of an upper bound effectively means ''mixed'' in general PHP terms, though we are not proposing the ability to explicitly type-hint as ''mixed'', which isn't supported by PHP.) Any valid PHP type-hint may be used as an upper bound, including simple types like ''int'', ''float'', ''bool'', ''string'' and ''object''. (Omission of an upper bound effectively means ''mixed'' in general PHP terms, though we are not proposing the ability to explicitly type-hint as ''mixed'', which isn't supported by PHP.)
  
-Note that the choice of the keyword ''is'' to indicate upper bounds is based on the rejection of perhaps more obvious alternatives - repurposing the ''extends'' or ''implements'' keywords would be misleading, since they would work precisely the same way; worse, permitting both keywords would render consumer code invalid if an upper bound type provided by a library is refactored between class and interface. Repurposing ''instanceof'' would also be misleading, since the upper bound is checking the type-hint, not an instance.+Note that the choice of the keyword ''is'' to indicate upper bounds is based on the rejection of perhaps more obvious alternatives - repurposing the ''extends'' or ''implements'' keywords would be misleading, since they would work precisely the same way; worse, permitting both keywords would render consumer code invalid if an upper bound type provided by a library is refactored between class and interface. Repurposing ''instanceof'' would also be misleading, since the upper bound is checking the type-hint, not an instance. Furthermore, we don't want this to collide with possible future mixed scalar types, such as ''number'' or ''scalar'', neither of which make sense in conjunction with either ''extends'' or ''implements''. (If a reserved ''is'' keyword is undesirable for other reasons, a simple '':'' is likely a better alternative than overloading the meaning of an existing keyword.)
  
 == Bounds Checking == == Bounds Checking ==
Line 221: Line 255:
 class Box<T> class Box<T>
 { {
-    use Box<T>;+    use Container<T>;
 } }
  
Line 257: Line 291:
 The first example is able to infer the type argument ''T'' as ''Hat'', because the type alias was used to type-hint the argument given for the ''$content'' parameter. The first example is able to infer the type argument ''T'' as ''Hat'', because the type alias was used to type-hint the argument given for the ''$content'' parameter.
  
-The second example results in a ''TypeError'', because the type parameter ''T'' was explicitly defined as ''string''. (Note that, if we had not used ''declare(strict_types=1)'', and if ''Box'' had implemented ''__toString()'', this would have been acceptable, due to the default behavior of weak scalar type-checking.)+The second example results in a ''TypeError'', because the type parameter ''T'' was explicitly defined as ''string''. (Note that, if we had not used ''declare(strict_types=1)'', and if ''Box'' had implemented ''<nowiki>__toString()</nowiki>'', this would have been acceptable, due to the default behavior of weak scalar type-checking.)
  
 Note the addition of ''func_type_args()'', which returns a list of type-hints pertaining to the current generic function call or constructor invocation. This complements ''func_get_args()'' by providing the list of type-arguments as fully-qualified class-names. Note the addition of ''func_type_args()'', which returns a list of type-hints pertaining to the current generic function call or constructor invocation. This complements ''func_get_args()'' by providing the list of type-arguments as fully-qualified class-names.
Line 295: Line 329:
  
 The same applies when overriding constructors and static methods. The same applies when overriding constructors and static methods.
 +
 +==== Generic Constructors ====
 +
 +Constructors may accept arbitrary type-arguments, just like any other method, e.g.:
 +
 +<code php>
 +class Hello<T1>
 +{
 +    public function __construct<T1,T2>()
 +    {
 +        // ...
 +    }
 +}
 +</code>
 +
 +In other words, the constructor may accept more type-arguments than those affecting the type.
  
 ==== Generic Closures ==== ==== Generic Closures ====
  
-TODO describe ''callable<T, ...>'' type-hints and generic ''Closure<T, ...>'' types+TODO describe ''callable<T, ...>'' type-hints and/or generic ''Closure<T, ...>'' and/or ''Function<T, ...>'' types
  
 ==== Type Checking ==== ==== Type Checking ====
Line 333: Line 383:
  
 TODO: decide whether or not [[https://en.wikipedia.org/wiki/Bounded_quantification|bounded polymorphism]] should be supported. TODO: decide whether or not [[https://en.wikipedia.org/wiki/Bounded_quantification|bounded polymorphism]] should be supported.
 +
 +=== Multiple Constraints ===
 +
 +TODO: decide whether or not multiple constraints should be supported, e.g. with a Java-like syntax:
 +
 +<code php>
 +class A<T> where T is T1, T is T2 {
 +    // ...
 +}
 +</code>
 +
 +This may relate to the [[https://wiki.php.net/rfc/union_types|union types RFC]] - if implemented, it may be more natural to expect support for union types as bounds.
  
 ==== Autoloading ==== ==== Autoloading ====
Line 346: Line 408:
 This RFC calls for the following changes and additions to the reflection API: This RFC calls for the following changes and additions to the reflection API:
  
-TODO+TODO (some [[https://gist.github.com/mindplay-dk/dc3d24eba8d13a650cc6|notes]] with ideas are available.)
  
 === Reification === === Reification ===
rfc/generics.txt · Last modified: 2018/06/02 17:33 by mindplay