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

Reply via email to