[fpc-pascal] Traits Proposal
Ryan Joseph
genericptr at gmail.com
Sun Feb 14 02:45:03 CET 2021
> On Feb 13, 2021, at 12:38 PM, Sven Barth <pascaldragon at googlemail.com> wrote:
>
> Right now, Ryan, your suggestion looks like a solution in search of a problem, or a "hey, look at that feature in language X, I so must have that in Pascal as well". Those concepts more likely then not tend to end in problems or should be rejected. So let's first define what we're trying to solve here:
What I'm trying to solve is the dilemma of OOP where we want to extend a class but the addition does not fit cleanly into an existing hierarchy. I know we've all had this problem and we were forced to settle on injecting some base class into a hierarchy even though other classes higher up don't need this functionality. Things like class helpers and interface delegation have already been added into the language so clearly there is a need not being addressed. We've all had this problem haven't we?
First off lets go summarize the differences/limitations of interface delegation (as it is now) when compared to good, old fashion normal inheritance and OOP:
1) Does not support fields, class operators, properties
2) Only supports classes so we're required to manage memory (more fragmented also which could be bad)
3) Requires you to define an interface with duplicate methods, so boilerplate code
4) Does not combine namespaces so you must cast the class as the interface or use the reference directly using dot notation (with performance implications?)
inheritance is so much easier and powerful. Just add the class name () and you're good to go. That's my starting point, what ever solution should be close to as easy as what already exists in OOP.
> === code begin ===
>
> type
> ITest = interface
> procedure Test;
> end;
>
> TTestImpl = record
> procedure Test;
> end;
>
> TTest = class(TObject, ITest)
> private
> fTest: TTestImpl;
> public
> property Test: TTestImpl read fTest implements ITest; default;
> end;
>
> var
> t: TTest;
> begin
> t := TTest.Create;
> try
> t.Test; // calls t.fTest.Test
> finally
> t.Free;
> end;
> end.
>
> === code end ===
>
> As the compiler needs to generate corresponding thunks anyway whether it needs to do this for a record or object is not really much more effort either.
>
> Whether the class needs to declare the interface in its interface clause can be argued about.
>
> But all in all one can see that with a few extensions of existing features one can easily provide a mixin-like, convenient functionality.
Great, this is getting closer to inheritance. It requires a default property (which is basically what traits were anyways) and a lifting of restrictions on the object being implemented. What we solved here is:
1) Default properties merge the namespaces, which is the perhaps the most important part of OOP, that is the class is "one thing". If there is one thing to accomplish it's this. Imagine how annoying OOP would be if you you have to do "sphere.circle.shape.Draw()" because we had TSphere which inherited from TCircle and in turn inherited from TShape.
2) If we can lift the restriction of classes-only by using records/objects then we don't need to write boilerplate to alloc/free classes and our fields are now contagious in memory. Again that would be so annoying if classes made us allocate and free the super class we were inheriting from.
What it leaves desired:
1) Making a dummy interface is annoying boiler plate but not a deal breaker.
2) No fields or properties although you have some solution below which will probably require some additional boiler plate. Class operators would are kind of sad to lose too but not a deal breaker.
I stand by that using some concept of traits/mixins does all the stuff we want with less boiler plate but I'm happy to explore any other ideas.
>
> Of course this does not provide any mechanism to directly add fields, however the compiler could in theory optimize access through property getters/setters if they're accessed through the surrounding class instance instead of the interface.
How does this look?
>
> Also this does not address the point of whether these delegates are able to access functionality of the surrounding class. In my opinion however this can be explicitely modelled by providing the class instance through a constructor or property or whatever.
Indeed but this can be solved by more boiler plate. :) In AfterConstruction you can set references as desired. I had other ideas on this as they related to traits but that wouldn't make sense if we were using an existing type like records or classes.
Regards,
Ryan Joseph
More information about the fpc-pascal
mailing list