I see. So it's the combination of not erroring when more parameters are passed than a function accepts, and permitting methods to add extra optional parameters that is wrong. So without the former being fix/deprecated, the correct thing to do is to disallow extra optional params. Makes sense.
Are there any valid use cases for passing extra parameters to a function any more, now that there is the varargs syntax to replace func_get_args()? If not, maybe it could be deprecated by this RFC. Regarding references, by-ref invariance for parameters makes sense, and is what method compatibility does. According to method compatibility and call-time behaviour however, return by-ref is effectively a subtype of return by-value: function &returns_ref() { $f = 0; return $f; } function returns_val() { return 1; } $v1 = returns_ref(); // ok $v2 =& returns_ref(); // ok $v3 = returns_val(); // ok $v4 =& returns_val(); // Notice: Only variables should be assigned by reference interface Methods { function &returnsRef(); function returnsVal(); } class ReturnRef implements Methods { function &returnsRef() {} // ok function &returnsVal() {} // ok } class ReturnVal implements Methods { function returnsRef() {} // Fatal error: Declaration of ReturnVal::returnsRef() must be compatible with & Methods::returnsRef() function returnsVal() {} // ok } Otherwise it LGTM cheers On Sat, Apr 23, 2016 at 8:37 PM, Nikita Nefedov <inefe...@gmail.com> wrote: > On Sat, 23 Apr 2016 10:46:38 +0300, Jesse Schalken <m...@jesseschalken.com> > wrote: > > This is great. The gaps in PHP's type annotations are slowly being filled. >> :) >> >> Are the rules for compatibility between callables the same rules for >> compatibility between methods in classes/interfaces? Because presently, >> this is allowed: >> >> interface Test { >> public function testMethod(); >> } >> >> function foo1(Test $x) { } >> >> foo1(new class implements Test { >> public function testMethod(int $extraParam = 0) { } >> }); >> >> >> but, according to the RFC, this would not be: >> >> function foo2(callable():void $x) { } >> >> foo2(function (int $extraParam = 0) { }); >> >> >> I don't see why they should be different. >> >> I also didn't see anything mentioning reference parameters/returns. I >> assume it just follows the rules for method compatibility? >> > > Hey Jesse, > > Thanks for reading the RFC. > > First and foremost - no, the rules for compatibility between callables > and method implementations are different at least because our > inheritance model currently doesn't support variance for every case: > > if you have > > class B extends A {} > > interface Foo { > function foo(B $b); > } > > then implementation of `foo()` can never have `foo(A $a)` signature, > which is crucial for callables for some cases. Note that callables > have runtime variance, where classes would have compile-time variance). > > So, in this way, callables have it right (it is desirable for classes > to have variance as well, but currently it's not possible as there is > a need for another compilation phase for verifying variance). > > What about your case of variance with extra optional parameter, it is > actually not type safe with the current way of how PHP works (it's > covered in the end of "Variance and Signature Validation" part). > The problem lies in the notion that passing extra arguments to the > function (the ones it doesn't even expect nor use) is considered > valid and doesn't throw an error. > So if you have a function that accepts optional argument of type B and > you pass this function as a parameter of type `callable() $cb`, then > it would be perfectly valid to call it as `$cb(new A);` because you > assume that $cb is of type `callable()` and that means if you pass > anything extra - it shouldn't Fatal. But in this case it would. > > While I agree that your use case is legit and ideally should be > supported, I don't think it is safe to support it until PHP deprecates > passing extra args to functions that don't expect them (which is very > troublesome to implement due to BC). > > Anyway, changing this behavior of callable type later on wouldn't > break BC so the gates will still be open. > > I've just added a part about references to the RFC (and found a > bug in the implementation, thanks for that =)), I'll just copy > and paste it here: > > Reference parameters are supported: no variance is applied to > the fact whether parameter is referential or not. > > Example: > > function foo(callable(&$byref) $cb) { } > > foo(function (&$bar) { }); // valid > foo(function ($bar) { }); // TypeError: Argument 1 passed to foo() > must be callable of compliant signature: callable(&$byref), callable($bar) > given > > 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 > > Functions returning a reference are not different from functions returning > a value in PHP, for the caller, hence both are interchangeable: > > function foo(callable(): A $cb) { } > > 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 > > (the last one has error in the current implementation, I'll fix it later) >