Hi Marco,

Thanks for the response.


On 21/10/2018 22:42, Marco Pivetta wrote:
 * If you need to proxy and add behavior, userland libraries already exist (mostly `doctrine/common` at https://github.com/doctrine/common/tree/a210246d286c77d2b89040f8691ba7b3a713d2c1/lib/Doctrine/Common/Proxy, `goaop/framework` at https://github.com/goaop/framework/tree/2.2.0 and `ocramius/proxy-manager` at https://github.com/Ocramius/ProxyManager/tree/2.2.2): if you aren't happy with those, a bit of `ReflectionClass#getMethods()` grunt work is just fine. We're still talking about edge cases anyway.


These are certainly interesting, but they feel rather too "heavyweight" for every day use. Imagine if you had to include a library which used reflection to generate a string and pass it to eval() every time you wanted to sub-class something!

Native AOP might come closer to what I was thinking about, but seems to be a very different way of thinking about code structure, rather than something that can be easily mixed with an existing architecture.


 * If you are proposing this syntax for performance reasons, most current delegation code has almost no noticeable overhead besides a new stack frame (possibly something the engine could optimise), and when it is present, it is to avoid I/O (much heavier anyway)

My main aim is to make composition feel more like a natural choice in the language. If I may cheekily use your own words against you:

> There will be less stuffing functionality in existing code via inheritance, which, in my opinion, is a symptom of haste combined with feature creep.

I have often heard it said that "it's a shame languages provide more features for inheritance than composition"; I was thinking about what features might redress that balance.


One problem with delegating calls this way, is that any new method implemented by an ancestor will not be delegated by default. A better solution would be to have a single `delegate` keyword that intercepts all method calls for methods not declared in this class, as if `__call` was implicitly implemented:


This is deliberate; implicitly copying everything from another class would just be reinventing (multiple) inheritance. If the delegated class adds a new public method, it should be a conscious decision whether to a) transparently delegate it; b) explicitly wrap it with custom functionality; or c) completely ignore it.


If you don't do it this way, you end up with a BC break any time an ancestor defines new public API. Yes, in this case these are interfaces, so it would already count as BC break, but if the parent is a class...


The point is not that the *ancestors* are interfaces, it's that the proxy is targeting an interface. Consider this scenario:

class LibraryImplementation implements FrameworkInterface, LibrarySpecificInterface {
    public function thingRequiredByFramework() {}
    public function additonalThing() {}
}

class UserlandWrapper implements FrameworkInterface {
    public LibraryImplementation $inner delegate { thingRequiredByFramework };
}

Although the wrapper is tied to a particular implementation (for whatever reason), it's only used in the context of the framework. If FrameworkInterface changes, then every class implementing it needs fixing, and that includes the UserlandWrapper (e.g, it might need to wrap a new method call with custom logic); but if LibrarySpecificInterface changes, and LibraryImplementation is updated, there's no relevance to UserlandWrapper.

LibraryImplementationis just a tool being used to implement FrameworkInterface; its other abilities are irrelevant.


Anyway, I do think that:

 1. the language already has all the tooling required to implement decorators correctly  2. the addition of a `delegate` functionality would cause more confusion for something that is already really trivial to implement/test/read


I think the existence of userland libraries like ProxyManager is rather good evidence that the language *doesn't* have very powerful tools for this. But yes, any new feature like this has to be useful enough to justify the extra complexity and learning curve; it may be that the version presented here isn't powerful enough to pass that bar.

Regards,

--
Rowan Collins
[IMSoP]

Reply via email to