Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:92652 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 95675 invoked from network); 23 Apr 2016 10:37:54 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 23 Apr 2016 10:37:54 -0000 Authentication-Results: pb1.pair.com header.from=inefedor@gmail.com; sender-id=pass Authentication-Results: pb1.pair.com smtp.mail=inefedor@gmail.com; spf=pass; sender-id=pass Received-SPF: pass (pb1.pair.com: domain gmail.com designates 209.85.215.67 as permitted sender) X-PHP-List-Original-Sender: inefedor@gmail.com X-Host-Fingerprint: 209.85.215.67 mail-lf0-f67.google.com Received: from [209.85.215.67] ([209.85.215.67:35231] helo=mail-lf0-f67.google.com) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 70/62-01429-1805B175 for ; Sat, 23 Apr 2016 06:37:53 -0400 Received: by mail-lf0-f67.google.com with SMTP id u64so4339076lff.2 for ; Sat, 23 Apr 2016 03:37:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=to:cc:subject:references:date:mime-version :content-transfer-encoding:from:message-id:in-reply-to:user-agent; bh=O70Hpdr6B7AcSjyjK2bSwTzrJTplrjPu6DPM+h2ri4c=; b=UJY0Z4AuuvfWhVq6r0rlNv4hFA+lU1u/ugmBEMR4lRETKOl4JYyNFCxSHcg5FoDeWS bYehbi4LJEl52GxZ43BCwczRIgAdrTMOPdPKRdeo9NxIGdrK4Rw2DcackyrhfY51WatO kAj5M4BUO5h4owspIfLsBNEv5SNEPW5ZvXTXZiYf4v3m/bCp9MYWPZ0p3gQ4y1+vCJET NKqMw1fc1WFLXP2Etj43mPGsQN0mcyH0DoVOAXJ/WolKtUfKBQPpy1ndJNvlIj9kC0Xc oEHidmj/sJwbSv/HR+bjiwAv8a8HmyGfZ3oRYFW7x1NqUM5bAHm02PR5oCgdiTeHKyX0 7Xuw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:to:cc:subject:references:date:mime-version :content-transfer-encoding:from:message-id:in-reply-to:user-agent; bh=O70Hpdr6B7AcSjyjK2bSwTzrJTplrjPu6DPM+h2ri4c=; b=XWatYmhs0SXXtfNW3C8GFAI8w7TwFsl0fGhzW2GCXPU2n9kULJrhg1C9fzsaR4PWEI E1urogOOCYMtroask2oOCQ8j9KCR+jkVrKJ8hj0GBljAZ7d5y7KWMi1GQQ8P65am0F8Q 8O0WuejJhVYyY95Z+4t+IiQs6370LpiI0GkGv6QtYlMBpPVJ0bFwPsw+sZNr6xpvou6U Go1OFy9xJ2CFh4o0g2Pook1qilkTti+UOa4Xct9MZylkniyaH0jfe+hxq+1LUk8HDxN9 5o2jgpcUP40G2jjQVb3gu0EPxI5tnZB4wWff4VL2JTwOTuSwjIRnirlYP433p93ZdY/I Xu9Q== X-Gm-Message-State: AOPr4FUQTS2pvYjfuxuX2IzsDnNI5VlcSWJ3j6QZhhAcmetbs65z273WNuj5vhB0B27fiA== X-Received: by 10.25.157.209 with SMTP id g200mr10507792lfe.101.1461407869706; Sat, 23 Apr 2016 03:37:49 -0700 (PDT) Received: from nikita-pc ([89.250.5.98]) by smtp.gmail.com with ESMTPSA id a134sm2470235lfb.1.2016.04.23.03.37.48 (version=TLS1 cipher=AES128-SHA bits=128/128); Sat, 23 Apr 2016 03:37:48 -0700 (PDT) Content-Type: text/plain; charset=utf-8; format=flowed; delsp=yes To: "Jesse Schalken" Cc: "PHP internals" References: Date: Sat, 23 Apr 2016 13:37:55 +0300 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Message-ID: In-Reply-To: User-Agent: Opera Mail/12.17 (Win32) Subject: Re: [PHP-DEV][RFC] Callable Types From: inefedor@gmail.com ("Nikita Nefedov") On Sat, 23 Apr 2016 10:46:38 +0300, Jesse Schalken 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)