rfc:callable-types
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
rfc:callable-types [2016/04/21 23:48] – more minor fixes marcio | rfc:callable-types [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Callable | + | ====== PHP RFC: Callable |
* Version: 1.0 | * Version: 1.0 | ||
* Date: 2015-08-27 | * Date: 2015-08-27 | ||
* Authors: Nikita Nefedov < | * Authors: Nikita Nefedov < | ||
- | * Status: | + | * Status: |
* First Published at: http:// | * First Published at: http:// | ||
Line 43: | Line 43: | ||
}); | }); | ||
// >>> | // >>> | ||
- | // TypeError: Argument 3 passed to reduce() must be callable(int, | + | // TypeError: Argument 3 passed to reduce() must be compliant with callable(int, |
</ | </ | ||
Line 137: | Line 137: | ||
}); | }); | ||
</ | </ | ||
+ | |||
+ | ==== Nested callables ==== | ||
+ | |||
+ | Nested callables can be used with no imposed limit on the nesting level. | ||
+ | <code php> | ||
+ | function foo(callable(callable(int)) $cb) { | ||
+ | $cb(function (int $i) { | ||
+ | var_dump($i); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | foo(function (callable(int) $intPrinter) { | ||
+ | $intPrinter(123); | ||
+ | }); | ||
+ | </ | ||
+ | |||
+ | There' | ||
+ | <code php> | ||
+ | function bar(callable(int $number): callable(int $number): callable(int $number): parent_callable $recursiveCb) { // this wouldn' | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | To add to that, nested callables can get pretty unreadable quickly, both of these problems would be best solved by a typedef feature of some kind, added to PHP later. | ||
==== Variance and Signature Validation ==== | ==== Variance and Signature Validation ==== | ||
Line 151: | Line 174: | ||
foo(function (A $a) {}); // there' | foo(function (A $a) {}); // there' | ||
- | foo(function (B $b) {}); // Uncaught TypeError: Argument 1 passed to foo() must be callable of compliant | + | foo(function (B $b) {}); // Uncaught TypeError: Argument 1 passed to foo() must be compliant |
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 | ||
</ | </ | ||
Line 157: | Line 180: | ||
The same rules apply to return type of a callable: | The same rules apply to return type of a callable: | ||
<code php> | <code php> | ||
- | function foo(callable: | + | function bar(callable() $cb) { } |
+ | bar(function (): A { return new A; }); // it is valid to pass a function with declared return type as a parameter of `callable(..)` | ||
+ | |||
+ | function foo(callable(): A $cb) { } | ||
foo(function (): A { return new A; }); // A == A | foo(function (): A { return new A; }); // A == A | ||
- | foo(function (): B { return new B; }); // B < A this closure will return narrower type than what is expected by " | + | foo(function (): B { return new B; }); // B < A this closure will return narrower type than what is expected by " |
</ | </ | ||
Line 169: | Line 195: | ||
</ | </ | ||
- | Optional | + | Optional |
<code php> | <code php> | ||
function foo(callable() $cb) { } | function foo(callable() $cb) { } | ||
Line 179: | 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 | ||
</ | </ | ||
+ | |||
+ | 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 | ||
+ | </ | ||
+ | The same goes for variadics as they are a special kind of optional parameters. | ||
When callable type is nested (when you have '' | When callable type is nested (when you have '' | ||
+ | |||
+ | ==== References in callables ==== | ||
+ | |||
+ | Reference parameters are supported: no variance is applied to the fact whether parameter is referential or not. | ||
+ | |||
+ | Example: | ||
+ | <code php> | ||
+ | function foo(callable(& | ||
+ | |||
+ | foo(function (&$bar) { }); // valid | ||
+ | foo(function ($bar) { }); // TypeError: Argument 1 passed to foo() must be compliant with callable(& | ||
+ | |||
+ | function bar(callable($byval) $cb) { } | ||
+ | |||
+ | bar(function (&$bar) { }); // TypeError: Argument 1 passed to bar() must be compliant with callable($byval), | ||
+ | </ | ||
+ | |||
+ | Functions returning a reference are compatible with functions returning a value for caller, hence both are interchangeable: | ||
+ | <code php> | ||
+ | function foo(callable(): | ||
+ | |||
+ | foo(function (): A { return new A; }); | ||
+ | foo(function &(): A { static $a; $a = $a ?: new A; return $a; }); // both would pass the boundaries of a type check | ||
+ | </ | ||
+ | |||
+ | There' | ||
+ | |||
+ | ==== Parameters with default values ==== | ||
+ | |||
+ | It's not possible to declare default value of a parameter in a callable prototype. Because currently PHP doesn' | ||
==== Syntax Choices ==== | ==== Syntax Choices ==== | ||
Line 272: | Line 335: | ||
function foo(callable($a, | function foo(callable($a, | ||
//... | //... | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Reflection ===== | ||
+ | |||
+ | There are no BC-breaking changes in Reflection. | ||
+ | |||
+ | Here are the changes needed for reflection: | ||
+ | |||
+ | `ReflectionParameter:: | ||
+ | |||
+ | <code php> | ||
+ | class ReflectionCallableType extends ReflectionType | ||
+ | { | ||
+ | /** | ||
+ | * Tells whether it's just a `callable` hint or if it has a prototype `callable(something): | ||
+ | */ | ||
+ | public function hasPrototype(): | ||
+ | |||
+ | /** | ||
+ | * Returns a number of parameters required by a callable prototype | ||
+ | */ | ||
+ | public function getArity(): bool; | ||
+ | |||
+ | /** | ||
+ | * Returns an array of ReflectionCallableParameter instances | ||
+ | */ | ||
+ | public function getParameters(): | ||
+ | |||
+ | /** | ||
+ | * Tells whether the prototype has return type defined | ||
+ | */ | ||
+ | public function hasReturnType(): | ||
+ | |||
+ | /** | ||
+ | * Returns return type of the callable prototype | ||
+ | */ | ||
+ | public function getReturnType(): | ||
+ | |||
+ | /** | ||
+ | * 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(): | ||
+ | | ||
+ | /** | ||
+ | * Whether this is a variadic parameter | ||
+ | */ | ||
+ | public function isVariadic(): | ||
} | } | ||
</ | </ | ||
Line 326: | 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=" |
+ | * Yes | ||
+ | * No | ||
+ | </doodle> | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
Line 364: | Line 506: | ||
- Scala | - Scala | ||
- Swift https:// | - Swift https:// | ||
- |
rfc/callable-types.1461282493.txt.gz · Last modified: 2017/09/22 13:28 (external edit)