> As a downside, you can't implement it in a normal class anymore and just use > an instance of that class.
I see that as a plus, because, as a you said: > That's why you call it with the method name of the interface Your logic is circular - you also said: > As a downside, you can't implement it in a normal class anymore and just use > an instance of that class. So you want it both ways, class and callable. But if I'm not supposed to invoke it as a callable, why implement it as a callable in the first place? In other words, you can't really have both - it doesn't really work: a callable can be treated as a class, but a class can't be treated as callable. If I have to treat everything as classes anyhow, why not just use classes? The only advantage seems to be shorter syntax than anonymous class declarations - the resulting objects have to be treated the same way in practice, e.g. you can't safely treat them as callables. That's where the chain falls off for me: you end up with callables, but you shouldn't depend on them being callables, so then why make them callables in the first place? The net result seems to be syntactic sugar for what is really just anonymous classes. I'd like to see an example of how this is really useful in practice - the example in the RFC seems rather hypothetical, and actually shows that the same thing is already possible with anonymous classes. Beyond syntax, where's the real advantage over actual classes or anonymous classes? On Sun, May 15, 2016 at 3:28 PM, Niklas Keller <m...@kelunik.com> wrote: > 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 >> > -- PHP Internals - PHP Runtime Development Mailing List To unsubscribe, visit: http://www.php.net/unsub.php