Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:93339 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 33603 invoked from network); 15 May 2016 13:28:35 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 15 May 2016 13:28:35 -0000 Authentication-Results: pb1.pair.com smtp.mail=me@kelunik.com; spf=permerror; sender-id=unknown Authentication-Results: pb1.pair.com header.from=me@kelunik.com; sender-id=unknown Received-SPF: error (pb1.pair.com: domain kelunik.com from 81.169.146.163 cause and error) X-PHP-List-Original-Sender: me@kelunik.com X-Host-Fingerprint: 81.169.146.163 mo4-p00-ob.smtp.rzone.de Received: from [81.169.146.163] ([81.169.146.163:54535] helo=mo4-p00-ob.smtp.rzone.de) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id 33/28-48938-18978375 for ; Sun, 15 May 2016 09:28:34 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1463318910; l=24628; s=domk; d=kelunik.com; h=Content-Type:Cc:To:From:Subject:Date:References:In-Reply-To: MIME-Version; bh=dfM5vPnpzNZ6AJBoeVcvZaFOjcSnpO82NNfg7TzONgw=; b=vLoAr8oEdbsgc5TMOHOBGLlMZoe8i5DJPpeA2jotBZaN2zEIxI4WzpimhDlIken+NhA 76AlZF3Ro831J9ZUMQcA1ZpiSHy2wKqKBTEGdg9XgL84G/EmqS+1BM1iAjkfhIILIfOFh 2KwyBPy6xtp7VFdMNuCjkFVWQES7FoFa3bs= X-RZG-AUTH: :IWkkfkWkbvHsXQGmRYmUo9mls2vWuiu+7SLGvomb4bl9EfHtO3E6 X-RZG-CLASS-ID: mo00 Received: from mail-wm0-f42.google.com ([74.125.82.42]) by smtp.strato.de (RZmta 37.26 AUTH) with ESMTPSA id Q053c2s4FDSUyj7 (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (curve secp384r1 with 384 ECDH bits, eq. 7680 bits RSA)) (Client did not present a certificate) for ; Sun, 15 May 2016 15:28:30 +0200 (CEST) Received: by mail-wm0-f42.google.com with SMTP id a17so97905522wme.0 for ; Sun, 15 May 2016 06:28:30 -0700 (PDT) X-Gm-Message-State: AOPr4FXOH/S4BXUfkRHnsvgvhJisxUHZ94gMtF+ddZBKVizWxopU7Lp/aHFTf+eV1JLeUzcrVlifQEK4JdADWQ== MIME-Version: 1.0 X-Received: by 10.194.62.99 with SMTP id x3mr24918561wjr.128.1463318910179; Sun, 15 May 2016 06:28:30 -0700 (PDT) Received: by 10.28.53.132 with HTTP; Sun, 15 May 2016 06:28:30 -0700 (PDT) In-Reply-To: References: <5734B148.40602@mvlabs.it> <925076c8-5d9c-a780-c260-f1c76855283c@gmail.com> <5734F21D.1050801@mvlabs.it> Date: Sun, 15 May 2016 15:28:30 +0200 X-Gmail-Original-Message-ID: Message-ID: To: Rasmus Schultz Cc: Marco Perone , PHP internals Content-Type: multipart/alternative; boundary=047d7b86db22d5cafe0532e17bf2 Subject: Re: [PHP-DEV] Functorial interfaces From: me@kelunik.com (Niklas Keller) --047d7b86db22d5cafe0532e17bf2 Content-Type: text/plain; charset=UTF-8 2016-05-15 12:05 GMT+02:00 Rasmus Schultz : > I know you're already voting on this, but I'd like to throw in a > couple of late comments. > > My main problem with this RFC is the use of the interface keyword for > something that almost by definition is not an interface. > > An interface is a set of methods, but an anonymous function is just > one function, so it can't possibly implement an interface - this only > works because of the single method constraint and some magical mapping > of the closure as a method implementing that single-method interface. > > You can't, for example, add another method to one of these > "interfaces", which is problematic - it's inconsistent with what > interfaces actually are. > > In a sense, what you've really done, is you've overloaded the > interface concept as a means of shoehorning typedefs into the > language. > > Per the example in the RFC: > > interface IFoo { > public function method() : int; > } > > $cb = function () implements IFoo : int { > return 42; > }; > > The coupling between $cb and IFoo is explicit - but the coupling > between IFoo::method() and the closure is implied; it's something the > interpreter, and the person reading the code, has to reason about. > > The one-method limitation isn't apparent from the "interface" > declaration - that is, nothing about the interface declaration implies > that it's not actually an interface, e.g. is not a set of methods. > > On the other hand, I can see the advantages of being able to treat and > invoke the closure as if it were an object implementing an interface - > it just seems like there's something missing. When a function > "implements" an interface, it puts a constraint on that interface, > preventing it from taking on another method - it imposes this > constraint on the interface from the outside-in, which seems really > counter intuitive to me. > > From the RFC: > > interface IFoo { > public function method1(); > public function method2(); > } > > function () implements IFoo {}; > > This is really surprising if you thought you understood what > interfaces are: a set of methods. > > What suppose I write an interface that has a single method today - and > consumers of my library begin to implement this interface using > closures in this manner. But then tomorrow, I add another method to my > interface? There is suddenly now direct upgrade path, no obvious way > to fix the code - they can't simply implement that new method and move > on, they now have to start refactoring everything from closures to > actual classes. > You can just convert the function to an anonymous class. If you add methods to an interface, that's a BC break for a major version, since it's not compatible anymore, everything needs an update either way. > In that sense, this is worse than typedefs - at least typedefs > consistently define precisely one function and not a set. > > Another disconnect from interfaces, is that objects can traditionally > implement more than one interface - but an anonymous function can only > implement a single interface. It's another deviation from actual > interfaces, overloading the interface" keyword - it's inconsistent > with interfaces, which is misleading and adds unnecessary learning > curve. > Literally, an interface is something you interact with. It's important for the consumer not the class implementing it. If you separate your concerns, you often have just one interface. > Another downside of this proposal, is that a single functional > interface effectively has two names: the type-name and the > method-name; but both names map to the same thing, the anonymous > closure. Method-names are necessary in interfaces because they > identify the individual methods in a set of methods. But functional > interfaces are not a set of methods. The functional interface > type-name already designates the type. There is no reason it should > need to have two names. > > How about the following alternative syntax? > > callable IFoo = function () : int; > > $cb = function () implements IFoo { > return 42; > }; > > With this syntactic sugar, functional interfaces are distinctly > single-method interfaces - you can't add another method. > > You no longer need to come up with two names for one thing. > > You don't need to repeat the return-type when implementing it. > I like that syntax, but it has its own disadvantages. > And you don't need to specify that extra method-name when invoking it: > As a downside, you can't implement it in a normal class anymore and just use an instance of that class. > if ($cb instanceof IFoo) { > $number = $cb(); > } > > It also avoids the following conundrum: > > interface Foo { > public function foo(); > } > > interface Bar { > public function bar(); > } > > class Bleet implements Foo, Bar { > // ... > } > > function fluff(Bleet $bleet) { > $bleet(); // ERROR! > } > > In this example, the fluff() function thinks it's getting an anonymous > function implementing Foo, but that's not the case. But it could be. > You opened that door when you decided to overload interfaces - not > only functions can implement these interfaces, classes can too. > That's why you call it with the method name of the interface. > I think this last example demonstrates a huge disconnect - you've > defined something that is apparently both an interface and a typedef > of sorts, but the resulting types are not in fact interchangible. That > is, you can't swap out an anonymous function with a class implementing > that interface, or vice-versa, unless the consumer relies on invoking > the method rather than using the object as a callable. The fact that > it's a callable isn't even a fact you can rely on anymore. > > I don't believe this is good. I think that the number of compiler > errors specified in the RFC alone is a flashing red light. > > I think that other languages have typedefs rather than a syntactic > version of single-method interface implementations. > > Is there a source of reference for this feature? I mean another > language implementing this pattern? > > I have a feeling other languages likely do not implement something like > this. > > I'm concerned that this might be one of those "original PHP features" > that turns out to be a little too original. > > I personally would prefer to have something that's more familiar, e.g. > something close to typedefs. > > Something that doesn't overload the concept of interfaces and requires > a big section in the manual explaining how interfaces in PHP are not > like interfaces in other languages, all the conditions, warnings, > acceptable use and limitations. > > I'd prefer something that's simpler and easier to explain. > > > On Thu, May 12, 2016 at 11:14 PM, Marco Perone wrote: > > On 12/05/2016 19:02, Rowan Collins wrote: > >> > >> On 12/05/2016 17:47, Andreas Heigl wrote: > >>> > >>> It's the other way around. > >>> > >>> The interface creates a contract that ensures that you can use ALL > >>> methods available in your SpecialClass. > >> > >> > >> I don't think that's what the interface in the example means: > >> > >> interface Foo > >> { > >> public function foo(SpecialClass $object); > >> } > >> > >> > >> What this says is "an object adhering to interface Foo has a method foo > >> which accepts any SpecialClass instance". > >> > >> In other words, it says that the following is bound to succeed: > >> > >> if ( $thing instanceOf Foo ) { > >> $thing->foo( new SpecialClass ); > >> } > >> > >> > >> Marco is right that a class with a wider definition fulfils this > contract: > >> > >> class Bar implements Foo > >> { > >> public function foo(BaseClass $object) > >> { > >> // do something with BaseClass $object > >> } > >> } > >> > >> $thing = new Bar; > >> if ( $thing instanceOf Foo ) { > >> $thing->foo( new SpecialClass ); > >> } > >> > >> > >> This is called "contravariance of parameters", and has come up a couple > of > >> times on the list. In particular, when return types were added, it was > >> discussed that they should technically be covariant (subtypes can > >> "narrow" return types, and say they only return a specific sub-type of > the > >> original contract). > >> > >> So from a design point of view, there is no reason not to support it; > >> unfortunately, there are some technical hurdles to implementing that > within > >> the PHP engine. Here is a nice summary from Anthony Ferrara: > >> http://marc.info/?l=php-internals&m=142791636808421&w=2 > >> > >> Regards, > > > > > > Thanks Rowan for your response and for the link, very interesting. > > > > Adding a bit of context to my question, I asked it after following a > > discussion in the FIG mailing list. > > In PSR-7 you have a RequestInterface and a ServerRequestInterface with > > inherits from it (the BaseClass and SpecialClass of my example). > > Now there is a discussion on how to standardize a MiddlewareInterface. > > You would like to have an interface like > > > > interface ServerMiddlewareInterface > > { > > public function __invoke(ServerRequestInterface $request, ...) > > } > > > > which plays the role of Foo, and maybe an implementation > > > > class MyMiddleware > > { > > public function __invoke(RequestInterface $request, ...) > > { > > // do something with $request > > } > > } > > > > which plays the part of Bar. > > > > Since at the moment Bar can not implement Foo, the solution to this is, > from > > a design point of view, a bit akward (see > > > https://github.com/php-fig/fig-standards/blob/95aece5e68376e9a5d79d2385d755824dfac3ed8/proposed/middleware.md#21-server-vs-client-requests > > for details). It would be nice if "variant" parameters and return types > were > > introduced, so the design could be improved (this is just an example, I > > guess there could be more aroud). > > > > Regards > > > > marco > > > > > > -- > > PHP Internals - PHP Runtime Development Mailing List > > To unsubscribe, visit: http://www.php.net/unsub.php > > > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: http://www.php.net/unsub.php > > --047d7b86db22d5cafe0532e17bf2--