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]