Interface delegates and the implements property specifier

classic Classic list List threaded Threaded
19 messages Options
Reply | Threaded
Open this post in threaded view
|

Interface delegates and the implements property specifier

Adriaan van Os-2
Section 7.4 Interface delegation
<https://www.freepascal.org/docs-html/current/ref/refse45.html#x98-1200007.4> of the FPC Language
Reference Guide discusses interface delegates and the implements property specifier.

For example

$interfaces corba}
type
   IMyInterface = interface
     procedure P1;
   end;

   IMyInterface2 = interface
     procedure P2;
   end;

   TMyClass = class(TInterfacedObject,
                    IMyInterface, IMyInterface2)
     FI2 : IMyInterface2;
   protected
     procedure IMyInterface.P1 = MyP1;
     procedure MyP1;
   public
     property MyInterface: IMyInterface2
        read FI2 implements IMyInterface2;

I had hoped that procedure IMyInterface2.P2 would now be visible as a method of TMyClass. This
would be quite helpful in implementing multiple-inheritance. But no, the implements specifier
completely hides it. I assume this has been discussed before.

However (after adding an identifying string to IMyInterface and IMyInterface2),
TObject.GetInterface( IMyInterface2) fails for the FI2 delegate but succeeds for an object of
TMyClass. This seems contradictory, as IMyInterface2 is really part of the F12 delegate, not of
TMyClass.

Regards,

Adriaan van Os

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
Am 21.12.2019 um 16:49 schrieb Adriaan van Os:

> Section 7.4 Interface delegation
> <https://www.freepascal.org/docs-html/current/ref/refse45.html#x98-1200007.4>
> of the FPC Language Reference Guide discusses interface delegates and
> the implements property specifier.
>
> For example
>
> $interfaces corba}
> type
>   IMyInterface = interface
>     procedure P1;
>   end;
>
>   IMyInterface2 = interface
>     procedure P2;
>   end;
>
>   TMyClass = class(TInterfacedObject,
>                    IMyInterface, IMyInterface2)
>     FI2 : IMyInterface2;
>   protected
>     procedure IMyInterface.P1 = MyP1;
>     procedure MyP1;
>   public
>     property MyInterface: IMyInterface2
>        read FI2 implements IMyInterface2;
>
> I had hoped that procedure IMyInterface2.P2 would now be visible as a
> method of TMyClass. This would be quite helpful in implementing
> multiple-inheritance. But no, the implements specifier completely
> hides it. I assume this has been discussed before.

Yes, this is by design, because the idea of the interface delegation is
that the class can be cast to the designated interface type (thus
supporting the interface) and then be used as if it directly implemented
the interface.

>
> However (after adding an identifying string to IMyInterface and
> IMyInterface2), TObject.GetInterface( IMyInterface2) fails for the FI2
> delegate but succeeds for an object of TMyClass. This seems
> contradictory, as IMyInterface2 is really part of the F12 delegate,
> not of TMyClass.

I don't get what you're saying here. Would you please provide code that
illustrates your problem?

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
In reply to this post by Adriaan van Os-2


> On Dec 21, 2019, at 10:49 AM, Adriaan van Os <[hidden email]> wrote:
>
> I had hoped that procedure IMyInterface2.P2 would now be visible as a method of TMyClass. This would be quite helpful in implementing multiple-inheritance. But no, the implements specifier completely hides it. I assume this has been discussed before.

That's exactly what I was ranting about some months back with the idea of "default properties". I would expect that syntax to pull the methods names into the current namespace and this would be very helpful.

It's by design as Sven pointed out but this makes no sense whatsoever to me. If the compiler team agrees I will personally make a mode switch or whatever is permitted to accomplish this. :)

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
Am 22.12.2019 um 04:44 schrieb Ryan Joseph via fpc-pascal:
>
>> On Dec 21, 2019, at 10:49 AM, Adriaan van Os <[hidden email]> wrote:
>>
>> I had hoped that procedure IMyInterface2.P2 would now be visible as a method of TMyClass. This would be quite helpful in implementing multiple-inheritance. But no, the implements specifier completely hides it. I assume this has been discussed before.
> That's exactly what I was ranting about some months back with the idea of "default properties". I would expect that syntax to pull the methods names into the current namespace and this would be very helpful.
>
> It's by design as Sven pointed out but this makes no sense whatsoever to me. If the compiler team agrees I will personally make a mode switch or whatever is permitted to accomplish this. :)
Vetoed.

You don't seem to understand what implementing an interface means in
Object Pascal. It means that a class can be cast to an interface. It
does *not* mean that the interface's methods are available from that class.

Take this:

=== code begin ===

program tintftest;

{$mode objfpc}
{$interfaces corba}

type
   ITest = interface
     procedure Test;
   end;

   TTest = class(TObject, ITest)
   strict private
     procedure Test;
   end;

procedure TTest.Test;
begin
end;

var
   t: TTest;
   i: ITest;
begin
   t := TTest.Create;
   //t.Test; // this does not compile
   i := t;
   i.Test;
end.

=== code end ===

An implementor can decide to have the interface's methods not accessible
from outside.

Or take this:

=== code begin ===

program tintftest;

{$mode objfpc}
{$interfaces corba}

type
   ITest = interface
     procedure Test;
   end;

   TTest = class(TObject, ITest)
   public
     procedure ITest.Test = IntfTest;
     procedure IntfTest;
     procedure Test;
   end;

procedure TTest.Test;
begin
   Writeln('Test');
end;

procedure TTest.IntfTest;
begin
   Writeln('IntfTest');
end;

var
   t: TTest;
   i: ITest;
begin
   t := TTest.Create;
   t.Test;
   i := t;
   i.Test;
end.

=== code end ===

This will output

=== output begin ===

PS C:\fpc\git> .\testoutput\tintftest
Test
IntfTest

=== output end ===

So while TTest implements the ITest interface it's methods are not the
same as the interface's.

Similar for delegates. The delegation property can be private (mostly
protected though to avoid a "unused private symbol" warning), so you
only get access to the delegated field by casting the class to the
implemented interface.

Your proposal to hoist the methods of a delegated interface would
conflict with this. Or it would require an alternative syntax to give
the implementor the necessary control of the visibility.

TL;DR: in Object Pascal a class implementing an interface can be cast to
that interface, but it does not need to present the methods of said
interface.

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 22, 2019, at 5:26 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> You don't seem to understand what implementing an interface means in Object Pascal. It means that a class can be cast to an interface. It does *not* mean that the interface's methods are available from that class.

I guess I'm not seeing the design pattern which they was invented for and I've never come across it in my own work. Not against the idea in any way however.

My mind went in the same direction as Adriaan's did when I saw "implements" I thought that one class could be built from many smaller classes but share the same namespace (like in multiple inheritance or entity/component designs). If a class implements an interface via a delegate then I would expect this to function the same as inheritance, i.e. the namespaces are merged and share functions. Doesn't that make sense?

Maybe what I mean to say is that there's a need for a delegation syntax that functions like multiple inheritance and avoids the traps of deeply nested single inheritance hierarchies. Does anyone else agree?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Adriaan van Os-2
Ryan Joseph via fpc-pascal wrote:
>
>
> I guess I'm not seeing the design pattern which they was invented for and I've never come across it in my own work. Not against the idea in any way however.
>
> My mind went in the same direction as Adriaan's did when I saw "implements" I thought that one class could be built from many smaller classes but share the same namespace (like in multiple inheritance or entity/component designs). If a class implements an interface via a delegate then I would expect this to function the same as inheritance, i.e. the namespaces are merged and share functions. Doesn't that make sense?
>
> Maybe what I mean to say is that there's a need for a delegation syntax that functions like multiple inheritance and avoids the traps of deeply nested single inheritance hierarchies. Does anyone else agree?

Compare this with relational databases where the columns of related, say "delegate", tables can be
put side-to-side with columns of the main table in so-called database views
<https://en.wikipedia.org/wiki/View_(SQL)>.

I have always wondered why hierarchies in object-oriented programming are idolized, where in the
database world hierarchical databases are something of the past and everything is relational there
now <https://en.wikipedia.org/wiki/Relational_database>.

Regards,

Adriaan van Os

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list
Ryan Joseph via fpc-pascal <[hidden email] ieb am Mo., 23. Dez. 2019, 06:01:


> On Dec 22, 2019, at 5:26 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> You don't seem to understand what implementing an interface means in Object Pascal. It means that a class can be cast to an interface. It does *not* mean that the interface's methods are available from that class.

I guess I'm not seeing the design pattern which they was invented for and I've never come across it in my own work. Not against the idea in any way however.

My mind went in the same direction as Adriaan's did when I saw "implements" I thought that one class could be built from many smaller classes but share the same namespace (like in multiple inheritance or entity/component designs). If a class implements an interface via a delegate then I would expect this to function the same as inheritance, i.e. the namespaces are merged and share functions. Doesn't that make sense?

It does make sense, but not in the context of interfaces. They are not there to provide implementations, but to provide a known API to the user. 



Maybe what I mean to say is that there's a need for a delegation syntax that functions like multiple inheritance and avoids the traps of deeply nested single inheritance hierarchies. Does anyone else agree?

What might be more interesting in this context than multiple inheritance is the concept of aspects (aka aspect oriented programming though you might also want to look for "mixin"; there's an old thread about that: https://lists.freepascal.org/fpc-pascal/2009-December/023815.html). If we could find an agreeable syntax and implementation for that then we'd be potentially inclined to include that as a new feature as there had been experiments for that in the past. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
In reply to this post by Adriaan van Os-2
Adriaan van Os <[hidden email]> schrieb am So., 22. Dez. 2019, 20:53:
I have always wondered why hierarchies in object-oriented programming are idolized, where in the
database world hierarchical databases are something of the past and everything is relational there
now <https://en.wikipedia.org/wiki/Relational_database>.

Because in object oriented programming languages one tends to try to work with the common denominator to ease up the dependencies between the concrete implementations. E.g. the LCL relies on TComponent and TControl, it does not need to know that there is a TMyWhateverControl. I even miss inheritance in databases as well as that would make the tables simpler. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list


> On Dec 23, 2019, at 2:02 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> What might be more interesting in this context than multiple inheritance is the concept of aspects (aka aspect oriented programming though you might also want to look for "mixin"; there's an old thread about that: https://lists.freepascal.org/fpc-pascal/2009-December/023815.html). If we could find an agreeable syntax and implementation for that then we'd be potentially inclined to include that as a new feature as there had been experiments for that in the past.
>

I never heard of "mixin" before but I'll study the wiki.

I assume that the compiler team has decided multiple inheritance is a bad idea correct? Personally I don't have enough experience to know but I see there is a need to delegate work between classes and share a common namespace. I'm happy with any way to achieve that.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 23, 2019, at 7:57 PM, Ryan Joseph <[hidden email]> wrote:
>
> I never heard of "mixin" before but I'll study the wiki.
>
> I assume that the compiler team has decided multiple inheritance is a bad idea correct? Personally I don't have enough experience to know but I see there is a need to delegate work between classes and share a common namespace. I'm happy with any way to achieve that.

Here's what I got from reading. I saw this concept of "trait" from PHP (didn't even know it existed until now) and I think it would look like this in Pascal. From what I gather the "trait" is new kind of object that merely is injected into an object but it can't itself be allocated or assigned. Does that sound like what you had in mind?



program mixin;

type
  TBrain = trait
    procedure Eat;
    procedure Fight;
  end;

type
  TPhysics = trait
    x, y, z: float;
    procedure Apply;
  end;

type
  TBase = class
    use TPhysics, TRendering, TBrain;
  end;

begin
end.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
Ryan Joseph via fpc-pascal <[hidden email]> schrieb am Di., 24. Dez. 2019, 02:47:


> On Dec 23, 2019, at 7:57 PM, Ryan Joseph <[hidden email]> wrote:
>
> I never heard of "mixin" before but I'll study the wiki.
>
> I assume that the compiler team has decided multiple inheritance is a bad idea correct? Personally I don't have enough experience to know but I see there is a need to delegate work between classes and share a common namespace. I'm happy with any way to achieve that.

Here's what I got from reading. I saw this concept of "trait" from PHP (didn't even know it existed until now) and I think it would look like this in Pascal. From what I gather the "trait" is new kind of object that merely is injected into an object but it can't itself be allocated or assigned. Does that sound like what you had in mind?

Basically, yes, though of course syntax, implementation and behavior need to be nicely defined first. For example there is the difference whether something is added at declaration time of the class or the creation time of the class (though that might be the difference between mixins and aspect oriented programming). 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 24, 2019, at 1:16 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> Basically, yes, though of course syntax, implementation and behavior need to be nicely defined first. For example there is the difference whether something is added at declaration time of the class or the creation time of the class (though that might be the difference between mixins and aspect oriented programming).
>

I hope you're all having a wonderful Christmas season. :)

Some more thoughts on this:

- My initial thought was that importing would happen at definition time simply because I don't know what the difference would be to import at declaration time.

- Importing fields/properties is easy because we can just add symbols to the symbol table but I'm not sure what it means in terms of the compiler to import a method from another class. The method needs to be cloned or perhaps synthesized as I've seen the term used in the compiler.  Because the traits aren't actually allocated like a class or record we need to copy the code out into the class that uses them.

program mixin;

type
  TBrain = trait
    procedure DoStuff; virtual;
  end;

procedure TBrain.DoStuff;
begin
  writeln('do something');
end;

type
  TBase = class
    use TBrain;
  end;

// TBase.DoStuff is synthesized from TBrain.DoStuff
procedure TBase.DoStuff;
begin
  writeln('do something');
end;

begin
end.


- What about generics? Looks complicated and dubious so maybe that could be a feature added later simply in the interest of taking things in small steps.

program mixin;

type
  generic TBrain<T> = trait
    procedure DoStuff(param: T); virtual;
  end;

type
  generic TBase<T> = class
    private
      use specialize TBrain<T>;
    public
      procedure DoStuff(param: T); override;
  end;

begin
end.

- Is overriding allowed? This presents a dilemma because we need to synthesize the method from the trait (or whatever they will be called) and allow an additional method which can call "inherited" and get the super method in the same class. This would be new functionally for the inherited keyword since the overriding is happening in the same class.

program mixin;

type
  TBrain = trait
    procedure DoStuff; virtual;
  end;

procedure TBrain.DoStuff;
begin
  writeln('do something');
end;

type
  TBase = class
    use TBrain;
    procedure DoStuff; override;
  end;

// TBase.TBrain_DoStuff is synthesized from TBrain.DoStuff
// and renamed to prevent name collisions
procedure TBase.TBrain_DoStuff;
begin
  writeln('do something');
end;

procedure TBase.DoStuff;
begin
  // inherited calls TBase.TBrain_DoStuff
  inherited;
  writeln('do more stuff');
end;

begin
end.




Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Adriaan van Os-2
Ryan Joseph via fpc-pascal wrote:

> - Importing fields/properties is easy because we can just add symbols to the symbol table but I'm not sure what it means in terms of the compiler to import a method from another class. The method needs to be cloned or perhaps synthesized as I've seen the term used in the compiler.  Because the traits aren't actually allocated like a class or record we need to copy the code out into the class that uses them.

The code of a "mixin" or "trait" or "delegate" (or whatever implementing-something) can be
referenced and it can put virtually into the namespace of an object. The one thing you cannot do
however, is copy the code, because then the basic problem of mutiple-inheritance pops-up again ---
which is what route for virtual methods to take in a multiple-inheritance "tree" that is no longer
a tree but a graph. For the same reason, methods overriding methods imported from the
implementing-something will still actually override the methods in the implementing-something, even
if they virtually seem to be in the namespace of the importing object.

Regards,

Adriaan van Os

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 27, 2019, at 8:21 AM, Adriaan van Os <[hidden email]> wrote:
>
> The code of a "mixin" or "trait" or "delegate" (or whatever implementing-something) can be referenced and it can put virtually into the namespace of an object. The one thing you cannot do however, is copy the code, because then the basic problem of mutiple-inheritance pops-up again --- which is what route for virtual methods to take in a multiple-inheritance "tree" that is no longer a tree but a graph. For the same reason, methods overriding methods imported from the implementing-something will still actually override the methods in the implementing-something, even if they virtually seem to be in the namespace of the importing object.

Should the traits/aspects even be overridable then? If they are it sounds like they need to be implemented the same as inheritance and this would basically just make them a different syntax for multiple inheritance. I don't think that's what Sven had it mind and if he did why not just do proper multiple inheritance using the existing syntax that interfaces uses?

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list
In reply to this post by Free Pascal - General mailing list
Am 26.12.2019 um 20:32 schrieb Ryan Joseph via fpc-pascal:

>
>> On Dec 24, 2019, at 1:16 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>>
>> Basically, yes, though of course syntax, implementation and behavior need to be nicely defined first. For example there is the difference whether something is added at declaration time of the class or the creation time of the class (though that might be the difference between mixins and aspect oriented programming).
>>
> I hope you're all having a wonderful Christmas season. :)
>
> Some more thoughts on this:
>
> - My initial thought was that importing would happen at definition time simply because I don't know what the difference would be to import at declaration time.

To be clear: with declaration time I mean when the type is written (" =
class ... end") versus creation time (either "var x: tmyclass" or "x :=
tmyclass .create"). The difference would be that the user of the class
would decide what is supported instead of the declarer of the class.
Adding something at creation time would of course require that the
functionality can only be accessed dynamically (similar to GetInterface,
though like that it can be hidden behind e.g. the "as" operator) and
that the functionality needs to be independant of the existing class.

> - Importing fields/properties is easy because we can just add symbols to the symbol table but I'm not sure what it means in terms of the compiler to import a method from another class. The method needs to be cloned or perhaps synthesized as I've seen the term used in the compiler.  Because the traits aren't actually allocated like a class or record we need to copy the code out into the class that uses them.

You don't need to do any copying of the functions. You need to partition
the instance so that fields that belong to a specific trait are grouped
together and then you pass an adjusted self pointer to the trait's
methods so that this only sees accesses its own fields (and properties
are the same as methods or field access: use the correct Self part).

What I'm still missing however is a real use case. What you have
presented as an example can just as easily be done with the existing
delegates. And just to avoid having to cast the instance to the
interface is in my opinion not enough reason for a new feature.

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 27, 2019, at 10:01 AM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> To be clear: with declaration time I mean when the type is written (" = class ... end") versus creation time (either "var x: tmyclass" or "x := tmyclass .create"). The difference would be that the user of the class would decide what is supported instead of the declarer of the class. Adding something at creation time would of course require that the functionality can only be accessed dynamically (similar to GetInterface, though like that it can be hidden behind e.g. the "as" operator) and that the functionality needs to be independant of the existing class.

Got it.

>
>> - Importing fields/properties is easy because we can just add symbols to the symbol table but I'm not sure what it means in terms of the compiler to import a method from another class. The method needs to be cloned or perhaps synthesized as I've seen the term used in the compiler.  Because the traits aren't actually allocated like a class or record we need to copy the code out into the class that uses them.
>
> You don't need to do any copying of the functions. You need to partition the instance so that fields that belong to a specific trait are grouped together and then you pass an adjusted self pointer to the trait's methods so that this only sees accesses its own fields (and properties are the same as methods or field access: use the correct Self part).

Can you explain or gives some function names of where/how the importation of fields/methods happens for inheritance? For fields I'm seeing that the struct data size expands when a super class is parsed and then in searchsym_in_class the correct symbol is found but I don't know what happens after that. The data must be copied at some point but I don't know when.

For method calling I think you're saying we merely pass a different self pointer depending on which class in the hierarchy makes the call.

>
> What I'm still missing however is a real use case. What you have presented as an example can just as easily be done with the existing delegates. And just to avoid having to cast the instance to the interface is in my opinion not enough reason for a new feature.

It's really just about the namespace and avoiding deeply nested this.that.do kind of syntax. We could accomplish this using just plain records, no need to even get involved with the complicated interface syntax. According to the wiki https://en.wikipedia.org/wiki/Trait_(computer_programming) lots of other languages are having this need to build component like systems and want a syntax that formalizes the concept.

Adriaan, what was your idea you had in mind when you brought this up?

========================

program test;

type
  TBrain = record
    procedure Eat;
    procedure Fight;
  end;

type
  TPhysics = record
    x, y, z: float;
    procedure Apply;
  end;

type
  TPerson = class
    physics: TPhysics;
    brain: TBrain;
  end;

var
  p: TPerson;
begin
  p.brain.DoThis;
  p.physics.x += 1;
end.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 27, 2019, at 10:29 AM, Ryan Joseph <[hidden email]> wrote:
>
> It's really just about the namespace and avoiding deeply nested this.that.do kind of syntax. We could accomplish this using just plain records, no need to even get involved with the complicated interface syntax. According to the wiki https://en.wikipedia.org/wiki/Trait_(computer_programming) lots of other languages are having this need to build component like systems and want a syntax that formalizes the concept.

I also wanted to mention this article which talks about default interface implementation (protocols in Swift) which exists in at least C# and Swift. Is that a viable route perhaps since we already have interfaces?

http://machinethink.net/blog/mixins-and-traits-in-swift-2.0/

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Adriaan van Os-2
In reply to this post by Free Pascal - General mailing list
Ryan Joseph via fpc-pascal wrote:

> Adriaan, what was your idea you had in mind when you brought this up?

Well, to give you an idea, here is an example (somewhat simplified for clarity). I am currently
porting MacApp to 64-bit. MacApp currently has

        TView = class( TEventHandler)

However, I have a TQDGraphPort class with QuickDraw emulation. For the QuickDraw emulation to work
with current application code, TQDGraphPort methods (like TQDGraphPort.MoveTo, TQDGraphPort.LineTo,
etcetera) have to be methods (or be in the namespace) of TView. So, I would really need

        TView = class( TQDGraphPort, TEventHandler)

The TEventHandler methods have to be methods (or be in the namespace) of TView, because existing
application code overrides the methods of TEventHandler in objects inheriting from TView. This
mechanism needs to continue working.

Other classes also inherit from TEventHandler: TDocument, TPrintHandler and TApplication.

I solved this now by defining four event-handling interfaces: IIdleHandler, IMenuHandler,
IKeyHandler, IMouseHandler, all inheriting from IEventHandler, Something like:

     IEventHandler                           =
       interface
           [ 'IEventHandler']

            function GetNextEventObj         : TObject;
       end;

     IKeyHandler                             =
       interface
         ( IEventHandler)
           [ 'IKeyHandler' ]

            function DoKeyCommand
               (     theChar                 : char;
                     theKeyCode              : Int16;
                 var theEventInfo            : EventInfo): TCommand;

            function DoCommandKey
               (     theChar                 : char;
                 var theEventInfo            : EventInfo): TCommand;
       end;

etcetera. The event-handling classes can now import the interfaces:

     TApplication = class( TRefCountObj, IKeyHandler, iMenuHandler, IIdleHandler)
     TDocument = class( TDBFile, IKeyHandler, iMenuHandler, IIdleHandler)
     TView = class( TMacAppGraphPort, IKeyHandler, IMenuHandler, IIdleHandler, IMouseHandler)

etcetera. The disadvantage of this approach is that for example a default DoKeyCommand must be
written three times, for TApplication, TDocument and TView, where the code for
TDocument.DoKeyCommand and TView.DoKeyCommand is identical.

Identical code is clumsy. Therefore my wish that a helper object could somehow be put in the
namespace of the importing object, including the ability to override the imported methods. I hope
that answers you question.

I admit however that with some clever interface programming, the duplicated code can be reduced in
the above case to a single line. As follows:

  function GetObjEventHandler
         (     theObj                  : TObject): IEventHandler;
       var
         theEventHandler               : IEventHandler;
     begin
       if ( theObj <> nil) and theObj.GetInterface
        ( IEventHandler, theEventHandler)
         then GetObjEventHandler       := theEventHandler
         else GetObjEventHandler       := nil
     end;

  function GetObjKeyHandler
         (     theObj                  : TObject): IKeyHandler;
       var
         theKeyHandler                 : IKeyHandler;
     begin
       if ( theObj <> nil) and theObj.GetInterface
        ( IKeyHandler, theKeyHandler)
         then GetObjKeyHandler         := theKeyHandler
         else GetObjKeyHandler         := nil
     end;

  function NextKeyCommand
         (     theObj                  : TObject;
               theChar                 : char;
               theKeyCode              : Int16;
           var theEventInfo            : EventInfo): TCommand;
       var
         theEventHandler               : IEventHandler;
         theKeyHandler                 : IKeyHandler;
     begin
       theKeyHandler                   := nil;
       while ( theObj <> nil) and ( theKeyHandler = nil) do
       begin
         theEventHandler               := GetObjEventHandler
           ( theObj);
         if theEventHandler = nil
           then theObj                 := nil
           else theObj                 := theEventHandler.GetNextEventObj;
         theKeyHandler                 := GetObjKeyHandler
           ( theObj)
       end;
       if theKeyHandler <> nil
         then NextKeyCommand           := theKeyHandler.DoKeyCommand
            ( theChar, theKeyCode, theEventInfo)
         else NextKeyCommand           := nil
     end;

  function TDocument.DoKeyCommand
         (     theChar                 : char;
               theKeyCode              : Int16;
           var theEventInfo            : EventInfo): TCommand;
     begin
       DoKeyCommand                    := NextKeyCommand
         ( self, theChar, theKeyCode, theEventInfo)
     end;

  function TView.DoKeyCommand
         (     theChar                 : char;
               theKeyCode              : Int16;
           var theEventInfo            : EventInfo): TCommand;
     begin
       DoKeyCommand                    := NextKeyCommand
         ( self, theChar, theKeyCode, theEventInfo)
     end;

Note that TDocument.DoKeyCommand and TView.DoKeyCommand are identical, but not larger than one
function call.

Regards,

Adriaan van Os

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Interface delegates and the implements property specifier

Free Pascal - General mailing list


> On Dec 27, 2019, at 1:39 PM, Adriaan van Os <[hidden email]> wrote:
>
> etcetera. The disadvantage of this approach is that for example a default DoKeyCommand must be written three times, for TApplication, TDocument and TView, where the code for TDocument.DoKeyCommand and TView.DoKeyCommand is identical.
>
> Identical code is clumsy. Therefore my wish that a helper object could somehow be put in the namespace of the importing object, including the ability to override the imported methods. I hope that answers you question.

This makes perfect sense to me but see what Sven says. I've encountered this same problem many times over the years where you want code modularity but can't use inheritance. There is absolutely a need for this syntax in my opinion and that fact Apple is holding developer conferences over it further shows this (https://developer.apple.com/videos/wwdc/2015/?id=408).

The interface delegation has so much boiler plate and doesn't help with the namespace issues so I'm not sure why it's better then making your TQDGraphPort and TEventHandler records and just including them as fields in the main class. I think they were designed for some other pattern that I'm not familiar with myself. The idea of traits/aspects is similar but falls under the umbrella of class composition as an alternative to inheritance.

The article I posted before (http://machinethink.net/blog/mixins-and-traits-in-swift-2.0/) actually has some good use case examples including pretty charts and pictures. Swift uses protocol extensions to accomplish "Protocol-Oriented Programming" which would be the equivalent of interface helpers in Object Pascal. We can't use fields in interfaces or helpers though so this won't work for us I'm afraid.

program mixin;

type
  IBrain = interface
    procedure Eat;
  end;

type
  IPhysics = interface
    procedure Apply;
  end;

type
  TPhysicsHelper = interface helper for IPhysics
    procedure Apply;
  end;

procedure TPhysicsHelper.Apply;
begin
  // ... we can't add fields in helpers or interfaces!
end;

type
  TPerson = class(IBrain, IPhysics)
  end;

begin
  // calls TPhysicsHelper.Apply via the IPhysics interface in TPerson
  person.Apply;
end.

Regards,
        Ryan Joseph

_______________________________________________
fpc-pascal maillist  -  [hidden email]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal