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)
>

Reply via email to