rfc:callable-types

Differences

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

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
rfc:callable-types [2016/04/26 12:12] – return reference nikita2206rfc:callable-types [2016/06/06 21:25] nikita2206
Line 1: Line 1:
-====== PHP RFC: Callable Types ======+====== PHP RFC: Callable Prototypes ======
   * Version: 1.0   * Version: 1.0
   * Date: 2015-08-27   * Date: 2015-08-27
   * Authors: Nikita Nefedov <inefedor@gmail.com>, Márcio Almada <marcio3w@gmail.com>   * Authors: Nikita Nefedov <inefedor@gmail.com>, Márcio Almada <marcio3w@gmail.com>
-  * Status: Under Discussion+  * Status: Declined
   * First Published at: http://wiki.php.net/rfc/callable-types   * First Published at: http://wiki.php.net/rfc/callable-types
  
Line 43: Line 43:
 }); });
 // >>> // >>>
-// TypeError: Argument 3 passed to reduce() must be callable(int, int):int, callable($a, $b, $c) given...+// TypeError: Argument 3 passed to reduce() must be compliant with callable(int, int):int, incompatible callable($a, $b, $c) given...
 </code> </code>
  
Line 174: Line 174:
  
 foo(function (A $a) {}); // there's no variance in this case, A can be substituted by A foo(function (A $a) {}); // there's no variance in this case, A can be substituted by A
-foo(function (B $b) {}); // Uncaught TypeError: Argument 1 passed to foo() must be callable of compliant signature: callable(A), callable(B $b) given+foo(function (B $b) {}); // Uncaught TypeError: Argument 1 passed to foo() must be compliant with callable(A), incompatible callable(B $b) given
 bar(function (A $a) {}); // callable(A) > callable(B) - we can substitute callable(B) with callable(A) because the latter has a wider input than the latter bar(function (A $a) {}); // callable(A) > callable(B) - we can substitute callable(B) with callable(A) because the latter has a wider input than the latter
 </code> </code>
Line 195: Line 195:
 </code> </code>
  
-Optional arguments count just like any other arguments:+Optional parameters count just like any other parameters if they are typed:
 <code php> <code php>
 function foo(callable() $cb) { } function foo(callable() $cb) { }
Line 205: Line 205:
 // And callable(A $a) < callable(), so the call to foo() will fail here // And callable(A $a) < callable(), so the call to foo() will fail here
 </code> </code>
 +
 +Otherwise they can pass type check boundaries even if they are not defined in the callable type:
 +<code php>
 +function foo(callable() $cb) { }
 +foo(function ($a = 123) { }); // valid as it won't have a problem explained above
 +</code>
 +The same goes for variadics as they are a special kind of optional parameters.
  
 When callable type is nested (when you have ''callable(callable(A))'') variance has to be inversed with each nesting level. So if we have ''callable(A) > callable(B)'' then ''callable(callable(A)) < callable(callable(B))''. When callable type is nested (when you have ''callable(callable(A))'') variance has to be inversed with each nesting level. So if we have ''callable(A) > callable(B)'' then ''callable(callable(A)) < callable(callable(B))''.
Line 217: Line 224:
  
 foo(function (&$bar) { }); // valid foo(function (&$bar) { }); // valid
-foo(function ($bar) { }); // TypeError: Argument 1 passed to foo() must be callable of compliant signature: callable(&$byref), callable($bar) given+foo(function ($bar) { }); // TypeError: Argument 1 passed to foo() must be compliant with callable(&$byref), incompatible callable($bar) given
  
 function bar(callable($byval) $cb) { } function bar(callable($byval) $cb) { }
  
-bar(function (&$bar) { }); // TypeError: Argument 1 passed to bar() must be callable of compliant signature: callable($byval), callable(&$bar) given+bar(function (&$bar) { }); // TypeError: Argument 1 passed to bar() must be compliant with callable($byval), incompatible callable(&$bar) given
 </code> </code>
  
Line 232: Line 239:
 </code> </code>
  
-There's no way to declare that you expect a callable that returns reference due to syntax problems.+There's no way to declare that you expect a callable that returns reference due to syntax limitations. 
 + 
 +==== Parameters with default values ==== 
 + 
 +It's not possible to declare default value of a parameter in a callable prototype. Because currently PHP doesn't consider parameter's default values in signature validation (their invariance is not enforced in overridden methods in classes).
  
 ==== Syntax Choices ==== ==== Syntax Choices ====
Line 324: Line 335:
 function foo(callable($a, $b):void $callback) { function foo(callable($a, $b):void $callback) {
   //...   //...
 +}
 +</code>
 +
 +===== Reflection =====
 +
 +There are no BC-breaking changes in Reflection.
 +
 +Here are the changes needed for reflection:
 +
 +`ReflectionParameter::getType()` can now return instance of `ReflectionCallableType` which extends `ReflectionType`:
 +
 +<code php>
 +class ReflectionCallableType extends ReflectionType
 +{
 +    /**
 +     * Tells whether it's just a `callable` hint or if it has a prototype `callable(something): something`
 +     */
 +    public function hasPrototype(): bool;
 +
 +    /**
 +     * Returns a number of parameters required by a callable prototype
 +     */
 +    public function getArity(): bool;
 +
 +    /**
 +     * Returns an array of ReflectionCallableParameter instances
 +     */
 +    public function getParameters(): array;
 +
 +    /**
 +     * Tells whether the prototype has return type defined
 +     */
 +    public function hasReturnType(): bool;
 +
 +    /**
 +     * Returns return type of the callable prototype
 +     */
 +    public function getReturnType(): ReflectionType;
 +
 +    /**
 +     * Tells whether $value has compatible callable prototype. This is the easiest way
 +     * to implement runtime-error-free compatibility checks at this point...
 +     * Later we could implement it in the form of `instanceof`
 +     */
 +    public function isA($value);
 +}
 +
 +class ReflectionCallableParameter
 +{
 +    /**
 +     * Tells whether this callable parameter has a type
 +     */
 +    public function hasType(): bool;
 +
 +    /**
 +     * Returns a type of this callable parameter
 +     */
 +    public function getType(): ReflectionType;
 +
 +    /**
 +     * Tells whether callable parameter is named or not (e.g. callable(Foo $foo) vs callable(Foo))
 +     */
 +    public function hasName(): bool;
 +
 +    /**
 +     * Returns name of the callable parameter
 +     */
 +    public function getName(): string;
 +
 +    /**
 +     * Whether this is by-val or by-ref parameter
 +     */
 +    public function isPassedByReference(): bool;
 +    
 +    /**
 +     * Whether this is a variadic parameter
 +     */
 +    public function isVariadic(): bool;
 } }
 </code> </code>
Line 378: Line 467:
 while PHP lacks first class packages. while PHP lacks first class packages.
  
-==== Reflection API ====+===== Votes =====
  
-An extension to the reflection API will be proposed in case the RFC is approved. +This RFC requires a 2/3 majority to pass. Vote started on May 23, 2016, ends June 6, 2016.
- +
-===== Votes =====+
  
-This RFC requires a 2/3 majority to pass.+<doodle title="Accept callable prototypes?" auth="nikita2206" voteType="single" closed="true"> 
 +   * Yes 
 +   * No 
 +</doodle>
  
 ===== Patches and Tests ===== ===== Patches and Tests =====
Line 416: Line 506:
     - Scala     - Scala
     - Swift https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html     - Swift https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Types.html
- 
rfc/callable-types.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1