2016-05-15 12:05 GMT+02:00 Rasmus Schultz <ras...@mindplay.dk>: > 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 <m.per...@mvlabs.it> 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 > >