Re: [fpc-pascal] Traits Proposal

2021-02-24 Thread Ryan Joseph via fpc-pascal
Ok my summary thus far with this syntax (default interface delegation)  is that 
composition is possible (which is very nice) but polymorphism is not really 
possible so it's not exactly an alternative to multiple inheritance. Besides 
the complications of implementing method resolution for classes (no idea what's 
involved here) I would say it's a pretty low time investment to get a good 
chunk of what people want from multiple inheritance, although not a full 
fledged alternative like adding a new "trait" syntax would provide.

I want to add more examples to the GitHub page so we don't forget. The most 
important thing to get right is not allowing any possibility to write have 
ambiguous methods. Here are some common ones I could think of. Are these 
correct? Anything to add? I guess the compiler just gives "already declared" 
errors for each method with a conflict. Does the visibility of the property 
matter in subclasses (I assume yes)?

()

type
  ICircle = interface
procedure Draw;
  end;

type
  TCircle = record
procedure Draw;
  end;

type
  TMyShape = class(ICircle)
private
  FCircle: TCircle;
public
  property Circle: TCircle read FCircle implements ICircle; default;
  end;

type
  TOtherShape = class(TMyShape)
public
  // ERROR - draw is already declared
  procedure Draw;
  end;

()

type
  TMyShape = class(ICircle)
private
  FCircle: TCircle;
public
  property Circle: TCircle read FCircle implements ICircle; default;

  // ERROR - Draw already declared
  procedure Draw;
  end;


()

type
  TMyShape = class(ICircle)
private
  FCircle: TCircle;
public
  property Circle: TCircle read FCircle implements ICircle; default;

  // Conflict is resolved, we can now redeclare Draw and Circle.Draw will 
point to TMyShape.Draw instead of TCircle.Draw
  procedure ICircle.Draw = Draw;
  procedure Draw;
  end;

()

type
  ISquare = interface
procedure Draw;
  end;

type
  TSquare = record
procedure Draw;
  end;

type
  TOtherShape = class(TMyShape, ISquare)
private
  FSquare: TSquare;
public
  // ERROR - Draw is already declared
  property Square: TSquare read FSquare implements ISquare; default;
  end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-22 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Mo., 22. Feb. 2021, 10:07:

>
>
> > On Feb 19, 2021, at 8:50 AM, Ryan Joseph  wrote:
> >
> > I just realized another potential problem. If we use the "default"
> keyword that means there could be multiple "defaults" unless we limit the
> property to 1 per class, which would kind of defeat the purpose of the
> feature (like the issue we had with helpers and made multi-helpers for).
>
> Can you respond to this concern? It could be a deal breaker for the syntax.
>

It depends on how "default" is defined. If we define it as "moves the
interface into the class' namespace" there is no problem as long as there
are no other conflicts (e.g. two interfaces with a Draw method without
further parameters that should still be handled by two different delegates
=> simply not possible).

I *think* Delphi allows multiple default array properties as long as they
have different index types (e.g. String vs Integer), though I'd need to
test this.

But independent of that I wouldn't have a problem to define "default" for a
delegation property in the way mentioned above.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-21 Thread Ryan Joseph via fpc-pascal


> On Feb 19, 2021, at 8:50 AM, Ryan Joseph  wrote:
> 
> I just realized another potential problem. If we use the "default" keyword 
> that means there could be multiple "defaults" unless we limit the property to 
> 1 per class, which would kind of defeat the purpose of the feature (like the 
> issue we had with helpers and made multi-helpers for).

Can you respond to this concern? It could be a deal breaker for the syntax.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Mattias Gaertner via fpc-pascal
On Fri, 19 Feb 2021 15:37:03 -0700
Ryan Joseph via fpc-pascal  wrote:

> > On Feb 19, 2021, at 3:14 PM, Sven Barth via fpc-pascal
> >  wrote:
> > 
> > TObject *is* always the root, but the first entry of the
> > inheritance list of a class *must* be a class as well.  
> 
> I'm testing interface delegation now in ObjFPC mode and I don't see
> that including TObject is required. 

In Delphi the first must be a class. FPC allows to omit the class
and uses TObject as default.

Mattias
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Ryan Joseph via fpc-pascal


> On Feb 19, 2021, at 3:14 PM, Sven Barth via fpc-pascal 
>  wrote:
> 
> TObject *is* always the root, but the first entry of the inheritance list of 
> a class *must* be a class as well.

I'm testing interface delegation now in ObjFPC mode and I don't see that 
including TObject is required. 

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Sven Barth via fpc-pascal

Am 19.02.2021 um 20:37 schrieb Ryan Joseph via fpc-pascal:



On Feb 19, 2021, at 11:01 AM, Sven Barth via fpc-pascal 
 wrote:

Your example is not quite correct and it's also not really complete: if a class 
implements an interface there *must* be a parent class mentioned (at least 
TObject). Also your example should demonstrate that one can call TMyShape.Draw 
without having to retrieve the interface (after all that's the point of the 
interface property).


TObject is always the root class I thought? Why is it different here?


TObject *is* always the root, but the first entry of the inheritance 
list of a class *must* be a class as well.



Additionally support for raw interfaces (aka Corba) should be added for 
interface delegation if it isn't already supported. (Otherwise your TMyShape 
either needs to inherit from e.g. TInterfacedObject or one of the involved 
types needs to implement the methods of IInterface.


They work already fortunately.


Good. :)

Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Marco van de Voort via fpc-pascal


Op 2021-02-19 om 16:50 schr

So the method resolution doesn't actually change the VMT table of the class, 
just the interface?


See it like this: An interface is a fragment of VMT. All methods of the 
interface are there in order.


If implemented right, it is like a pointer into the implementing 
classes' VMT table.


The IVT is a mapping of interface identifiers (in COM terms, GUIDS) 
mapped to the offset into the VMT they apply too.




___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Ryan Joseph via fpc-pascal


> On Feb 19, 2021, at 11:01 AM, Sven Barth via fpc-pascal 
>  wrote:
> 
> Your example is not quite correct and it's also not really complete: if a 
> class implements an interface there *must* be a parent class mentioned (at 
> least TObject). Also your example should demonstrate that one can call 
> TMyShape.Draw without having to retrieve the interface (after all that's the 
> point of the interface property). 
> 

TObject is always the root class I thought? Why is it different here?

> Additionally support for raw interfaces (aka Corba) should be added for 
> interface delegation if it isn't already supported. (Otherwise your TMyShape 
> either needs to inherit from e.g. TInterfacedObject or one of the involved 
> types needs to implement the methods of IInterface. 
> 

They work already fortunately.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Fr., 19. Feb. 2021, 17:32:

> Btw, here's point of reference for this other proposed syntax.
>
> https://github.com/genericptr/freepascal/wiki/Default-Implements-Property


Your example is not quite correct and it's also not really complete: if a
class implements an interface there *must* be a parent class mentioned (at
least TObject). Also your example should demonstrate that one can call
TMyShape.Draw without having to retrieve the interface (after all that's
the point of the interface property).

Additionally support for raw interfaces (aka Corba) should be added for
interface delegation if it isn't already supported. (Otherwise your
TMyShape either needs to inherit from e.g. TInterfacedObject or one of the
involved types needs to implement the methods of IInterface.

Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Ryan Joseph via fpc-pascal
Btw, here's point of reference for this other proposed syntax.

https://github.com/genericptr/freepascal/wiki/Default-Implements-Property

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-19 Thread Ryan Joseph via fpc-pascal


> On Feb 18, 2021, at 11:30 PM, Sven Barth  wrote:
> 
> The only thing that will not work and which will continue not to work is that 
> m_circle.Draw will not be called if you access the TMyShape through a 
> TShape.Draw call. For that one needs to use an explicit declaration:
> 
> === code begin ===
> 
> type
>   TMyShape = class(TShape, ICircle)
>   private
> m_circle: TCircle;
>   public
> procedure ICircle.Draw = Draw;
> property Circle: TCircle read m_circle implements ICircle;
> procedure Draw; override;
>   end;
> 
> procedure TMyShape.Draw;
> begin
>   m_circle.Draw;
> end;
> 
> === code end ===


So the method resolution doesn't actually change the VMT table of the class, 
just the interface? That's unfortunate because being able to affect the VMT by 
method resolution would open up a lot of possibilities for polymorphism using 
composition. Maybe writing wrapper functions is good enough for this. Not sure, 
I need to do some research into old code and explore things a bit more.


I just realized another potential problem. If we use the "default" keyword that 
means there could be multiple "defaults" unless we limit the property to 1 per 
class, which would kind of defeat the purpose of the feature (like the issue we 
had with helpers and made multi-helpers for).

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Sven Barth via fpc-pascal

Am 18.02.2021 um 23:58 schrieb Ryan Joseph via fpc-pascal:



On Feb 18, 2021, at 3:07 PM, Sven Barth  wrote:


So "class type method resolution" is what's missing? I never used the interface 
method resolution so I don't really understand this feature. What needs to happen as far 
as the compiler is concerned?

The problem is this: if you have a property with an interface type the compiler 
can simply build the interface's VMT for this. However for classes (or objects 
or records) the compiler needs to generate slightly different thunks that fixup 
the correct Self value, especially if things are mixed between the subclass and 
the container class.

Then this is the real challenge to implement a "default implements property" 
and something I know nothing about. :) The rest is pretty easy actually though.


Correct, that is the real challenge ;)



type
   TMyShape = class(TShape, ICircle)
 private
   m_circle: TCircle;
 protected
   procedure ICircle.Draw = Draw;
 public
   property circle: TCircle read m_circle implements ICircle;
   end;

Your specific example is rather useless, cause your interface only has a single 
method that you redirect to the class' method thus you wouldn't need to use 
interface delegation. But anyway, your code would generate a runtime error, 
because your TMyShape.Draw would still be abstract.

Maybe it's a bad example but I'm trying to illustrate how method resolution 
could be used for overriding (or what ever it would be called in this context.

I know this isn't correct syntax but it's more of this direction:

procedure Draw =  ICircle.Draw;

TShape.Draw is resolved by ICircle.Draw, which is implemented via TCircle. It's 
quasi-overriding but the VMT table should point to m_circle.Draw so Draw could be called 
on TShape and get property resolved by the "implements ICircle" property. Hope 
that makes sense.
Let me get it straight: your example is that both TShape and TCircle 
provide a Draw method, but in TMyShape you want TCircle.Draw to be used 
instead of TShape.Draw when TMyShape.Draw is called. Correct so far?


Well that would work without any additional declarations. The idea would 
be that the "default" modifier would lead to conflicts only if a Draw 
method exists in TMyShape itself. If it's declared in one of the parents 
then that won't matter.


The only thing that will not work and which will continue not to work is 
that m_circle.Draw will not be called if you access the TMyShape through 
a TShape.Draw call. For that one needs to use an explicit declaration:


=== code begin ===

type
  TMyShape = class(TShape, ICircle)
  private
    m_circle: TCircle;
  public
    procedure ICircle.Draw = Draw;
    property Circle: TCircle read m_circle implements ICircle;
    procedure Draw; override;
  end;

procedure TMyShape.Draw;
begin
  m_circle.Draw;
end;

=== code end ===

This way it does not matter if TCircle is a class, a record or an 
object, cause it will always work correctly.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Martin Frb via fpc-pascal

On 18/02/2021 23:47, Ryan Joseph via fpc-pascal wrote:



On Feb 18, 2021, at 12:33 PM, Martin Frb via fpc-pascal 
 wrote:

   TMyFoo = class(specialize TTrait)
  procedure Foo;
   end;

Of course that can get out of hand, if you want to include many traits.

I'm not really understand this at all. You're still using subclassing, which is 
the thing we're trying to avoid.


Yes and Yes, and maybe Why?

I agree that a dedicated traits solution would be cleaner, and may have 
advances.
For example in the above, the trait can not have a base-trait. (Well it 
can, but it get messy, really messy).

Multiple traits via generic => messy.
And also the conflict resolution behaviour differs.
...


However what is the problem of the subclassing?

1) From the user of the class (the person using the class in their code) 
all methods are in the desired namespace.


2) There is (almost) no performance penality.
Well perfomance:
  SomeFoo is T will need to step the extra classes inbetween.
  And so will  fpc assert objec checks, if enbled. (but thats debugging)

But otherwise I can't think were the resulting class would even need one 
CPU tick more. Calling a method (with the exact same signature) should 
generate the exact same code, independent if it was declared in the 
current or in the base class. (at least if it is not 
overriden/reintroduced in the current, but then you would need to call 
it with the "inherited" keyword, so that would be different.)

none virtual method => known address, just call
virtual method => address from know offset in the VMT. In both cases the 
VMT of the current class. Same thing.



And yes, their is memory. Their is one single block of memory for the 
class data. (It is only once, even if you have 1000 instances). So what?


One could have a WPO optimization, to remove classes from the tree, if 
they are not needed.
- no reference to the class itself: casting, ".. is .." (not possible 
here, since anonymous)

- access via ClassType, ParentClassType etc
- no difference in the VMT compared to the next higher class
- not sure maybe others
But its a lot of work in order to save a few bytes in the exe.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Ryan Joseph via fpc-pascal


> On Feb 18, 2021, at 3:07 PM, Sven Barth  wrote:
> 
>> So "class type method resolution" is what's missing? I never used the 
>> interface method resolution so I don't really understand this feature. What 
>> needs to happen as far as the compiler is concerned?
> 
> The problem is this: if you have a property with an interface type the 
> compiler can simply build the interface's VMT for this. However for classes 
> (or objects or records) the compiler needs to generate slightly different 
> thunks that fixup the correct Self value, especially if things are mixed 
> between the subclass and the container class.

Then this is the real challenge to implement a "default implements property" 
and something I know nothing about. :) The rest is pretty easy actually though.

> 
>> type
>>   TMyShape = class(TShape, ICircle)
>> private
>>   m_circle: TCircle;
>> protected
>>   procedure ICircle.Draw = Draw;
>> public
>>   property circle: TCircle read m_circle implements ICircle;
>>   end;
> 
> Your specific example is rather useless, cause your interface only has a 
> single method that you redirect to the class' method thus you wouldn't need 
> to use interface delegation. But anyway, your code would generate a runtime 
> error, because your TMyShape.Draw would still be abstract.

Maybe it's a bad example but I'm trying to illustrate how method resolution 
could be used for overriding (or what ever it would be called in this context.

I know this isn't correct syntax but it's more of this direction:

procedure Draw =  ICircle.Draw;

TShape.Draw is resolved by ICircle.Draw, which is implemented via TCircle. It's 
quasi-overriding but the VMT table should point to m_circle.Draw so Draw could 
be called on TShape and get property resolved by the "implements ICircle" 
property. Hope that makes sense.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Ryan Joseph via fpc-pascal


> On Feb 18, 2021, at 12:33 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
>   TMyFoo = class(specialize TTrait)
>  procedure Foo;
>   end;
> 
> Of course that can get out of hand, if you want to include many traits.

I'm not really understand this at all. You're still using subclassing, which is 
the thing we're trying to avoid.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Martin Frb via fpc-pascal

On 18/02/2021 22:42, Sven Barth wrote:


You need to constrain T as a TObject, then it works.


Ah that works. With 2 restrictions

TBase can be any class, since a trait can be applied to any class.
So the only known common base is TObject.

(restriction 1)
But "TBase: TObject" means that the trait can not access Self.Base, even 
if the latter passed in type has such a method.

Same with (note the "class" keyword)
 generic TTrait = class(TBase)


Normally if the param to a generic is not restricted you can write 
T.anything, and during specialization this will be resolved or fail.

So that ability is lost.

As a workaround THost can be passed in.
(restriction 2)
That requires a forward declaration of TFoo. Otherwise it fails.


type
  TFooBase = class
    procedure Base;
  end;

  generic TTrait = class(TBase)
    procedure Bar;
  end;

  TMyFoo = class;
  TMyFoo = class(specialize TTrait)
 procedure Foo;
  end;


procedure TTrait.Bar;
begin
  //Self.Base; // fails
  THost(Self).Base;
  THost(Self).Foo;
end;

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Sven Barth via fpc-pascal

Am 17.02.2021 um 16:22 schrieb Ryan Joseph via fpc-pascal:



On Feb 17, 2021, at 6:10 AM, Sven Barth  wrote:

Simply because no one has come around to implement it yet. The class type case 
is more complicated than the interface case. (Same would be true for record and 
objects)
So, yes, we'll need to implement this first which would finally bring FPC up to 
Delphi compatibility regarding interface delegation.

So "class type method resolution" is what's missing? I never used the interface 
method resolution so I don't really understand this feature. What needs to happen as far 
as the compiler is concerned?


The problem is this: if you have a property with an interface type the 
compiler can simply build the interface's VMT for this. However for 
classes (or objects or records) the compiler needs to generate slightly 
different thunks that fixup the correct Self value, especially if things 
are mixed between the subclass and the container class.



What if you want to implement virtual methods in a base class? This is kind of the core 
of "composition over inheritance" so it needs to be handled via method 
resolution I assume.




(*
  * Shape
  *)

type
   TShape = class
 protected
   procedure Draw; virtual; abstract;
   end;

(*
  * Circle
  *)

type
   ICircle = interface
 procedure Draw;
   end;

type
   TCircle = class(ICircle)
 procedure Draw;
   end;

(*
  * My Shape
  *)

type
   TMyShape = class(TShape, ICircle)
 private
   m_circle: TCircle;
 protected
   procedure ICircle.Draw = Draw;
 public
   property circle: TCircle read m_circle implements ICircle;
   end;


Your specific example is rather useless, cause your interface only has a 
single method that you redirect to the class' method thus you wouldn't 
need to use interface delegation. But anyway, your code would generate a 
runtime error, because your TMyShape.Draw would still be abstract.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Sven Barth via fpc-pascal

Am 18.02.2021 um 20:33 schrieb Martin Frb via fpc-pascal:

could be written as

  generic TTrait = class(T)
    procedure Bar;
  end;

  TMyFoo = class(specialize TTrait)
 procedure Foo;
  end;

Of course that can get out of hand, if you want to include many traits.


You need to constrain T as a TObject, then it works.

Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Martin Frb via fpc-pascal

On 18/02/2021 18:46, Ryan Joseph via fpc-pascal wrote:



On Feb 18, 2021, at 10:40 AM, Benito van der Zander via fpc-pascal 
 wrote:

Traits are like reverse type helpers. With the type helper you first declare 
the class and then the extending helper.

Indeed, but with the crucial distinction that helpers don't allow fields, only 
methods.


And they are differently scoped.


There is another approach, but generics do not yet support that

type
  TFooBase = class
  end;

  TTrait = trait
    procedure Bar;
  end;

  TFoo = class(TFooBase)
    _trait: TTrait;
 procedure Foo;
  end;

could be written as

  generic TTrait = class(T)
    procedure Bar;
  end;

  TMyFoo = class(specialize TTrait)
 procedure Foo;
  end;

Of course that can get out of hand, if you want to include many traits.

-

On the other hand, it even would allow support for the Trait to access 
methods from TFoo...


  generic TTrait = class(TBase)
    procedure Bar; // can do    THost(self).Foo()
  end;

  TMyFoo = class(specialize TTrait)
 procedure Foo;  // can call Bar()
  end;

Of course assuming that TMyFoo can already be passed during specialization.


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Ryan Joseph via fpc-pascal


> On Feb 18, 2021, at 10:40 AM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> Traits are like reverse type helpers. With the type helper you first declare 
> the class and then the extending helper.

Indeed, but with the crucial distinction that helpers don't allow fields, only 
methods.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-18 Thread Benito van der Zander via fpc-pascal

Hi,

The problem is that, we like OOP inheritance  but when we extend classes we are 
forced into a single hierarchy.


there is another way to extend classes without inheritance: type helpers

Traits are like reverse type helpers. With the type helper you first 
declare the class and then the extending helper.


=== code begin ===
type
  TTest = class(TObject)
  public
    procedure test(a: integer);
  end;
  TTrait = class helper for TTest
    procedure test2;
  end;

procedure TTest.test(a: integer);
begin
  writeln(a);
end;

procedure TTrait.test2;
begin
  test(10);
end;
//var x: ttest2;
//x.test2

=== code end ===


With traits you could first define the extension, and then the class:

=== code begin ===
type
  TTrait = trait
    procedure test2;
  end;
  TTest = class(TObject, TTrait)
  public
    procedure test(a: integer);
  end;

procedure TTest.test(a: integer);
begin
  writeln(a);
end;

procedure TTrait.test2;
begin
  test(10); //here it needs to know the test function exist. Perhaps 
declare it in the the trait, too?

end;
//var x: ttest2;
//x.test2
=== code end ===

But otherwise, it looks exactly the same

Perhaps the type helper code could be reused for traits





Cheers,
Benito


On 18.02.21 04:37, Ryan Joseph via fpc-pascal wrote:




On Feb 17, 2021, at 9:59 AM, Adriaan van Os via fpc-pascal 
 wrote:

1. multiple inheritance is nice to have, but it has the big issue that the 
inheritance tree becomes an inheritance graph and that makes overrules 
ambiguent.
2. interfaces don't have this issue with multiple inheritance, because they 
just declare, not implement
3. but that is also the weakness of interfaces,  as we don't want to 
reimplement the same code each time
4. so, we really want an multiple-inheritance graph at the declaration level 
with clear tree-like unambigous inheritance paths at the implementation level
5. thus, the idea is to "push-in" implementation code into an interface that 
integrates fully at the declaration level but is independent at the implementation level.

I would say that's right.

The problem is that, we like OOP inheritance  but when we extend classes we are forced 
into a single hierarchy. We could use existing delegation patterns and dot-notation  like 
obj.helper.DoSomething or by breaking out entirely and using plain functions  but then we 
lose some of what makes OOP nice, which is, simply saying 
"something.DoFunction". It may be trivial in terms of typing but it's elegant 
and in my opinion clean code which is not tedious to write makes happy and more 
productive.


Here's a more practical example but there are other possibilities for 
composition patterns (I'l think of examples later). I copied this from the RTL 
and made some changes. Assume you have this hierarchy and you want to add some 
methods/data to JPEG and GIF images but not TIFF and PNG (they have some 
special compression needs or something like that). What we would do now is 
probably just dump them into TCustomBitmap (and bloat TIFF/PNG)  or make 
another subclass just to store the extra methods (which is useless besides 
being a method store for 2 specific other classes).

What I want with traits is that I can extend those 2 classes with specific 
functionality, still retain the object-orientedness of the syntax and not get 
trapped trying to inject stuff into a hierarchy where it doesn't really belong 
anyways.


TTIFFBitmap   TPNGBitmap   TJPEGBitmap   TGIFBitmap
 |
 TCustomBitmap
 |
 TRasterImage
 |
 TGraphic
 |
 TPersistent
 |
 TObject


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal



> On Feb 17, 2021, at 9:59 AM, Adriaan van Os via fpc-pascal 
>  wrote:
> 
> 1. multiple inheritance is nice to have, but it has the big issue that the 
> inheritance tree becomes an inheritance graph and that makes overrules 
> ambiguent.
> 2. interfaces don't have this issue with multiple inheritance, because they 
> just declare, not implement
> 3. but that is also the weakness of interfaces,  as we don't want to 
> reimplement the same code each time
> 4. so, we really want an multiple-inheritance graph at the declaration level 
> with clear tree-like unambigous inheritance paths at the implementation level
> 5. thus, the idea is to "push-in" implementation code into an interface that 
> integrates fully at the declaration level but is independent at the 
> implementation level.

I would say that's right.

The problem is that, we like OOP inheritance  but when we extend classes we are 
forced into a single hierarchy. We could use existing delegation patterns and 
dot-notation  like obj.helper.DoSomething or by breaking out entirely and using 
plain functions  but then we lose some of what makes OOP nice, which is, simply 
saying "something.DoFunction". It may be trivial in terms of typing but it's 
elegant and in my opinion clean code which is not tedious to write makes happy 
and more productive.


Here's a more practical example but there are other possibilities for 
composition patterns (I'l think of examples later). I copied this from the RTL 
and made some changes. Assume you have this hierarchy and you want to add some 
methods/data to JPEG and GIF images but not TIFF and PNG (they have some 
special compression needs or something like that). What we would do now is 
probably just dump them into TCustomBitmap (and bloat TIFF/PNG)  or make 
another subclass just to store the extra methods (which is useless besides 
being a method store for 2 specific other classes). 

What I want with traits is that I can extend those 2 classes with specific 
functionality, still retain the object-orientedness of the syntax and not get 
trapped trying to inject stuff into a hierarchy where it doesn't really belong 
anyways.


TTIFFBitmap   TPNGBitmap   TJPEGBitmap   TGIFBitmap
| 
TCustomBitmap
| 
TRasterImage
| 
TGraphic
| 
TPersistent
| 
TObject


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Adriaan van Os via fpc-pascal

To streamline the discussion, let me recapitulate:

1. multiple inheritance is nice to have, but it has the big issue that the inheritance tree becomes 
an inheritance graph and that makes overrules ambiguent.

2. interfaces don't have this issue with multiple inheritance, because they 
just declare, not implement
3. but that is also the weakness of interfaces,  as we don't want to reimplement the same code each 
time
4. so, we really want an multiple-inheritance graph at the declaration level with clear tree-like 
unambigous inheritance paths at the implementation level
5. thus, the idea is to "push-in" implementation code into an interface that integrates fully at 
the declaration level but is independent at the implementation level.


So, the idea (and the purpose of the discussion) I think is to implement (5) in 
an elegant way.

Regards,

Adriaan van Os

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 6:10 AM, Sven Barth  wrote:
> 
> Simply because no one has come around to implement it yet. The class type 
> case is more complicated than the interface case. (Same would be true for 
> record and objects)
> So, yes, we'll need to implement this first which would finally bring FPC up 
> to Delphi compatibility regarding interface delegation. 

So "class type method resolution" is what's missing? I never used the interface 
method resolution so I don't really understand this feature. What needs to 
happen as far as the compiler is concerned?

What if you want to implement virtual methods in a base class? This is kind of 
the core of "composition over inheritance" so it needs to be handled via method 
resolution I assume.




(*
 * Shape
 *)

type
  TShape = class
protected
  procedure Draw; virtual; abstract;
  end;

(*
 * Circle
 *)

type
  ICircle = interface
procedure Draw;
  end;

type
  TCircle = class(ICircle)
procedure Draw;
  end;

(*
 * My Shape
 *)

type
  TMyShape = class(TShape, ICircle)
private
  m_circle: TCircle;
protected
  procedure ICircle.Draw = Draw;
public
  property circle: TCircle read m_circle implements ICircle;
  end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Ryan Joseph via fpc-pascal


> On Feb 17, 2021, at 6:17 AM, Sven Barth  wrote:
> 
> No. With wrapper function he means the thunks that are responsible for 
> adjusting the instance pointer of the interface to the instance pointer of 
> the class before jumping to the method of the class. 

Oh, so this is some extra code that runs every time you call a method of an 
interface? I understand interfaces and use them sometimes, but not all the 
internal workings in the compiler and how they're implemented.

> I have the feeling that you don't really know how interfaces work.
> ARC would have the exact same penalty as the reference counting of 
> interfaces, because that is essentially what the compiler would use: call 
> some function that increases/decreases the reference count of the instance. 
> That is *exactly* what is done for interfaces. 
> 
> Also the interface is not created. It's part of the class instance. You 
> simply have a pointer sized value that points to that. In the ideal situation 
> of *only* working with the interface you can simply forget the class 
> instance, cause the class will be freed once the reference count reached 0. 
> Thus there will be *no* additional storage involved.

I'm talking about the decoupling of the interface and class and how that's a 
strange way to implement ARC. These wrapper functions and runtime lookups for 
casting are another side of it. I would hope someday we can implement ref 
counting in classes the same way as dynamic arrays and ansistrings, i.e. you 
just pass the reference and forget about it.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Marco van de Voort via fpc-pascal


Op 2021-02-17 om 00:02 schreef Benito van der Zander via fpc-pascal:


And there often is a lot of unintentional deep copying. This is also 
why a property returning a record is fairly useless except for 
extremely small records like TPoint (and even that is not optimal no 


But a managed record to replace an interface, would only contain a 
single pointer/class ref. That can be copied fast


(1) A record is not a pointer. So that would require some implicit 
referencing in the property


(2) If it was managed, it would be an allocation, so I don't understand 
this.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Mi., 17. Feb. 2021, 02:21:

>
>
> > On Feb 16, 2021, at 3:35 PM, Benito van der Zander via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> > But it is not calling the method, it is calling the wrapper function
>
> what you say wrapper function do you mean the ref counting functions when
> you pass around the interface?
>

No. With wrapper function he means the thunks that are responsible for
adjusting the instance pointer of the interface to the instance pointer of
the class before jumping to the method of the class.

I guess for ref counting you're supposed to cast the class to the interface
> (which is an expensive runtime lookup) and then pass that thing around?
> That seems like a crazy way to get ref counting plus you need to cast the
> interface back to the class if you want to call other methods? I still
> don't get this pattern. ARC should be handled by the compiler and be on the
> class itself, not some additional object you need to create and then store.
>

I have the feeling that you don't really know how interfaces work.
ARC would have the exact same penalty as the reference counting of
interfaces, because that is essentially what the compiler would use: call
some function that increases/decreases the reference count of the instance.
That is *exactly* what is done for interfaces.

Also the interface is not created. It's part of the class instance. You
simply have a pointer sized value that points to that. In the ideal
situation of *only* working with the interface you can simply forget the
class instance, cause the class will be freed once the reference count
reached 0. Thus there will be *no* additional storage involved.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-17 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Mi., 17. Feb. 2021, 02:15:

>
>
> > On Feb 16, 2021, at 2:43 PM, Sven Barth via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> > FPC currently does not yet support class types not to mention records
> and objects which is what I had mentioned earlier already.
>
> Yes I see this on https://freepascal.org/docs-html/ref/refse47.html. Why
> isn't this supported? I guess as part of this proposal we need to implement
> this feature as well?  I'm not familiar with this area of the language so
> I'm not sure what this entails.
>

Simply because no one has come around to implement it yet. The class type
case is more complicated than the interface case. (Same would be true for
record and objects)
So, yes, we'll need to implement this first which would finally bring FPC
up to Delphi compatibility regarding interface delegation.


> >
> >> It's a strange syntax compared to anything else we allow in classes and
> the boiler plate is really adding up now. "procedure Draw" is going to be
> in 4 lines now!
> >>
> >> It would be nicer if they made this into one line so we don't have yet
> another set of duplicate method signatures, i.e.:
> >>
> >>   procedure Draw as ICircle.Draw;
> >
> > I can't shake the feeling that all you complain about is writing even a
> little bit too much. Pascal is *not* about using as less code as possible,
> but to be as correct as possible and the syntax of interface delegation
> allows exactly that.
>
> Being too verbose is clunky in my opinion. That method resolution syntax
> is just kind of an odd addition for classes to my eyes. "procedure
> Type.Name = Name;" doesn't have any precedence inside classes/records so it
> stands out.
>

Again, the syntax is much more powerful than what you suggested. Also it's
the existing syntax and not something new. Not to mention that it only
needs to be used if you need to implement a method yourself.
Iff(!) we find out down the line once we have the basic functionality
working, that people are struggling with this, we can still think about
improving something, but for now I'd prefer to add as less new syntaxes as
possible (especially if a syntax that does provide this exact functionality
*does* exist already, no matter if it's too verbose for some).

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 3:35 PM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> But it is not calling the method, it is calling the wrapper function

what you say wrapper function do you mean the ref counting functions when you 
pass around the interface?

I guess for ref counting you're supposed to cast the class to the interface 
(which is an expensive runtime lookup) and then pass that thing around? That 
seems like a crazy way to get ref counting plus you need to cast the interface 
back to the class if you want to call other methods? I still don't get this 
pattern. ARC should be handled by the compiler and be on the class itself, not 
some additional object you need to create and then store. 

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 2:43 PM, Sven Barth via fpc-pascal 
>  wrote:
> 
> FPC currently does not yet support class types not to mention records and 
> objects which is what I had mentioned earlier already.

Yes I see this on https://freepascal.org/docs-html/ref/refse47.html. Why isn't 
this supported? I guess as part of this proposal we need to implement this 
feature as well?  I'm not familiar with this area of the language so I'm not 
sure what this entails.

> 
>> It's a strange syntax compared to anything else we allow in classes and the 
>> boiler plate is really adding up now. "procedure Draw" is going to be in 4 
>> lines now!
>> 
>> It would be nicer if they made this into one line so we don't have yet 
>> another set of duplicate method signatures, i.e.:
>> 
>>   procedure Draw as ICircle.Draw;
> 
> I can't shake the feeling that all you complain about is writing even a 
> little bit too much. Pascal is *not* about using as less code as possible, 
> but to be as correct as possible and the syntax of interface delegation 
> allows exactly that.

Being too verbose is clunky in my opinion. That method resolution syntax is 
just kind of an odd addition for classes to my eyes. "procedure Type.Name = 
Name;" doesn't have any precedence inside classes/records so it stands out.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Benito van der Zander via fpc-pascal

Hi,


And there often is a lot of unintentional deep copying. This is also 
why a property returning a record is fairly useless except for 
extremely small records like TPoint (and even that is not optimal no 


But a managed record to replace an interface, would only contain a 
single pointer/class ref. That can be copied fast




Bye,
Benito
On 16.02.21 21:49, Marco van de Voort via fpc-pascal wrote:


Op 2021-02-16 om 21:33 schreef Ryan Joseph via fpc-pascal:


On Feb 16, 2021, at 1:27 PM, Marco van de Voort via fpc-pascal 
 wrote:


And there often is a lot of unintentional deep copying. This is also 
why a property returning a record is fairly useless except for 
extremely small records like TPoint (and even that is not optimal no
deep copying? You mean from ref counted types like "array of" and 
ansistring?


No, use of TP objects and records in e.g. properties in not carefully 
crafted code. They are a lot less userfriendly.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Sven Barth via fpc-pascal
Benito van der Zander via fpc-pascal 
schrieb am Di., 16. Feb. 2021, 23:35:

> Interfaces are not slow because they are are interfaces! When you call a
> interface method it has the same costs as a virtual method call! And the
> cost for reference counting that interfaces have right now would be there
> for ARC as well.
>
>
> But it is not calling the method, it is calling the wrapper function
>
> That is a additional second method call/jump for every interface method
> call.
>
>
> E.g. _AddRef on TInterfacedObject as an interface, does not call
> TInterfacedObject._AddRef, it calls this:
> WRPR_$SYSTEM_$$_TINTERFACEDOBJECT_$_IUNKNOWN_$_1_$_SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT
>
> 00424600 4883ef10 sub$0x10,%rdi
> 00424604 e997f8feff   jmpq   0x413ea0
> 
>

It's an *unconditional* jump. The branch prediction of the CPU should
handle this without much penalty.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Benito van der Zander via fpc-pascal

Hi,

If you need to create 1000 class instances each frame then you have a 
flaw in your logic in my opinion.



I have more like a million class instances

For my XPath stuff, and every returned value is put in a variant-like 
class. Selecting all nodes on an GB large XML, could even create almost 
a billion class instances



I once did a benchmark. It is 10% faster to reuse the class instances 
rather than recreating them, my commit log says. And I only implemented 
one interface (actually, two interfaces, since IUnknown always gets 
pulled in, too. )



Interfaces are not slow because they are are interfaces! When you call 
a interface method it has the same costs as a virtual method call! And 
the cost for reference counting that interfaces have right now would 
be there for ARC as well.



But it is not calling the method, it is calling the wrapper function

That is a additional second method call/jump for every interface method 
call.



E.g. _AddRef on TInterfacedObject as an interface, does not call 
TInterfacedObject._AddRef, it calls this:


WRPR_$SYSTEM_$$_TINTERFACEDOBJECT_$_IUNKNOWN_$_1_$_SYSTEM$_$TINTERFACEDOBJECT_$__$$__ADDREF$$LONGINT 


00424600 4883ef10 sub    $0x10,%rdi
00424604 e997f8feff   jmpq   0x413ea0 




Bye,
Benito
On 16.02.21 19:48, Sven Barth via fpc-pascal wrote:
Ryan Joseph via fpc-pascal > schrieb am Di., 16. Feb. 
2021, 19:21:


>
> There we have:
>
> * slower creation of the class, because each implemented
interface adds another VMT reference to the class which needs to
be initialized.

How bad is this? We need to make a test case we can profile. For
example if you have a game that creates 1000 classes 60 frames per
second. If adding interfaces to the classes hurts this process we
may have a deal breaker.


If you need to create 1000 class instances each frame then you have a 
flaw in your logic in my opinion.



> * slow reference counting. Especially if it is thread safe and
exception safe with the implicit exception block

It's a whole other topic but FPC needs opt-in ARC at the language
level for classes. Interfaces are not only slow but then you need
to pass around a reference to the interface instead of the class
you actually care about. It makes no sense to me whatsoever.


Interfaces are not slow because they are are interfaces! When you call 
a interface method it has the same costs as a virtual method call! And 
the cost for reference counting that interfaces have right now would 
be there for ARC as well.


Regards,
Sven


___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Sven Barth via fpc-pascal

Am 16.02.2021 um 19:53 schrieb Ryan Joseph via fpc-pascal:



On Feb 16, 2021, at 11:44 AM, Sven Barth  wrote:

I wasn't saying anything that contradicts this, only that the original 
mechanism of the delegation will be available even with the default modifier 
and this mechanism will in fact be necessary to access them through RTTI.


Can you give an example of this syntax? I get errors like Interface "ICircle" cannot be 
delegated by "TMyShape", it already has method resolutions.


FPC currently does not yet support class types not to mention records 
and objects which is what I had mentioned earlier already.



It's a strange syntax compared to anything else we allow in classes and the boiler plate 
is really adding up now. "procedure Draw" is going to be in 4 lines now!

It would be nicer if they made this into one line so we don't have yet another 
set of duplicate method signatures, i.e.:

   procedure Draw as ICircle.Draw;


I can't shake the feeling that all you complain about is writing even a 
little bit too much. Pascal is *not* about using as less code as 
possible, but to be as correct as possible and the syntax of interface 
delegation allows exactly that.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Marco van de Voort via fpc-pascal


Op 2021-02-16 om 21:33 schreef Ryan Joseph via fpc-pascal:



On Feb 16, 2021, at 1:27 PM, Marco van de Voort via fpc-pascal 
 wrote:

And there often is a lot of unintentional deep copying. This is also why a 
property returning a record is fairly useless except for extremely small 
records like TPoint (and even that is not optimal no

deep copying? You mean from ref counted types like "array of" and ansistring?


No, use of TP objects and records in e.g. properties in not carefully 
crafted code. They are a lot less userfriendly.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 1:27 PM, Marco van de Voort via fpc-pascal 
>  wrote:
> 
> And there often is a lot of unintentional deep copying. This is also why a 
> property returning a record is fairly useless except for extremely small 
> records like TPoint (and even that is not optimal no

deep copying? You mean from ref counted types like "array of" and ansistring?

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Marco van de Voort via fpc-pascal


Op 2021-02-16 om 19:21 schreef Ryan Joseph via fpc-pascal:

Interfaces are extremely slow. Virtual method calls are also slow. I have been 
using interfaces for reference counting, and have been thinking to replace it 
all with managed records because they are so slow (unfortunately records can 
also be slow because fpc does not always keep them in registers even if they 
are pointer sized)


And there often is a lot of unintentional deep copying. This is also why 
a property returning a record is fairly useless except for extremely 
small records like TPoint (and even that is not optimal no



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ralf Quint via fpc-pascal

On 2/16/2021 10:48 AM, Sven Barth via fpc-pascal wrote:
Ryan Joseph via fpc-pascal > schrieb am Di., 16. Feb. 
2021, 19:21:


>
> There we have:
>
> * slower creation of the class, because each implemented
interface adds another VMT reference to the class which needs to
be initialized.

How bad is this? We need to make a test case we can profile. For
example if you have a game that creates 1000 classes 60 frames per
second. If adding interfaces to the classes hurts this process we
may have a deal breaker.


If you need to create 1000 class instances each frame then you have a 
flaw in your logic in my opinion.


Yup, that would be a deal breaker right from the get go...

Ralf




--
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 11:48 AM, Sven Barth  wrote:
> 
> If you need to create 1000 class instances each frame then you have a flaw in 
> your logic in my opinion. 
> 

You can reuse classes and reset fields but getting a new piece of memory could 
be faster. These things do happen and adding on class allocation performance 
penalties could be a problem. I need to profile first to know what we're 
talking about here.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 11:44 AM, Sven Barth  wrote:
> 
> I wasn't saying anything that contradicts this, only that the original 
> mechanism of the delegation will be available even with the default modifier 
> and this mechanism will in fact be necessary to access them through RTTI. 
> 

Can you give an example of this syntax? I get errors like Interface "ICircle" 
cannot be delegated by "TMyShape", it already has method resolutions.


It's a strange syntax compared to anything else we allow in classes and the 
boiler plate is really adding up now. "procedure Draw" is going to be in 4 
lines now!

It would be nicer if they made this into one line so we don't have yet another 
set of duplicate method signatures, i.e.:

  procedure Draw as ICircle.Draw;


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Di., 16. Feb. 2021, 19:21:

> >
> > There we have:
> >
> > * slower creation of the class, because each implemented interface adds
> another VMT reference to the class which needs to be initialized.
>
> How bad is this? We need to make a test case we can profile. For example
> if you have a game that creates 1000 classes 60 frames per second. If
> adding interfaces to the classes hurts this process we may have a deal
> breaker.
>

If you need to create 1000 class instances each frame then you have a flaw
in your logic in my opinion.


> > * slow reference counting. Especially if it is thread safe and exception
> safe with the implicit exception block
>
> It's a whole other topic but FPC needs opt-in ARC at the language level
> for classes. Interfaces are not only slow but then you need to pass around
> a reference to the interface instead of the class you actually care about.
> It makes no sense to me whatsoever.
>

Interfaces are not slow because they are are interfaces! When you call a
interface method it has the same costs as a virtual method call! And the
cost for reference counting that interfaces have right now would be there
for ARC as well.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Sven Barth via fpc-pascal
Ryan Joseph via fpc-pascal  schrieb am
Di., 16. Feb. 2021, 19:22:

>
>
> > On Feb 15, 2021, at 11:41 PM, Sven Barth via fpc-pascal <
> fpc-pascal@lists.freepascal.org> wrote:
> >
> > Again, the point of the interface would be to control which methods and
> properties of the delegated  type would be available. Also it would allow
> for seamless integration with the RTTI as it's possible to query the
> implemented interface of a class and to retrieve a reference to it, thus it
> would be possible to access this at runtime as well.
> >
> > The compiler will generate a VMT for the interface with method thunks
> that adjust the Self pointer so that it works correctly. The
> GetInterfaceByEntry function uses this static data to generate an adjusted
> Self value that callers can use.
>
> As far as the "default implements property" is concerned we don't actually
> need, or even want to utilize the interfaces themselves. The idea of
> leveraging the interface syntax for exporting method names is one thing but
> if we actually have to use the interfaces internally then we open a whole
> can of worms in terms of performance.  Class instantiation may be taking on
> some significant baggage now also as the compiler sets up these interface
> VMT tables, even if the user never needs or wants them.
>
> For example this code should be a compile time indirection instead of a
> hidden interface cast, right? The method forwarding syntax which works by
> modifying the VMT can hopefully be done at compile time for the default
> property also.
>

I wasn't saying anything that contradicts this, only that the original
mechanism of the delegation will be available even with the default
modifier and this mechanism will in fact be necessary to access them
through RTTI.

Regards,
Sven

>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 16, 2021, at 6:51 AM, Benito van der Zander via fpc-pascal 
>  wrote:
> 
> Interfaces are extremely slow. Virtual method calls are also slow. I have 
> been using interfaces for reference counting, and have been thinking to 
> replace it all with managed records because they are so slow (unfortunately 
> records can also be slow because fpc does not always keep them in registers 
> even if they are pointer sized)

It's a real concern indeed. What we're proposing with a default property 
doesn't need to have these impacts but the class instantiation is going to be 
affected I think. 

> 
> There we have:
> 
> * slower creation of the class, because each implemented interface adds 
> another VMT reference to the class which needs to be initialized.

How bad is this? We need to make a test case we can profile. For example if you 
have a game that creates 1000 classes 60 frames per second. If adding 
interfaces to the classes hurts this process we may have a deal breaker.

> 
> * slower method calling because of the wrapper function

You mean for fields? We can inline those I believe but it's not fun boiler 
plate for sure.

> 
> * extremely slow casting an interface to the class. Never cast an interface, 
> better add a method to the interface returning a class reference
> 

We can avoid casting using the default property but it will still be possible 
to cast the class if you want to. 


> * slow reference counting. Especially if it is thread safe and exception safe 
> with the implicit exception block

It's a whole other topic but FPC needs opt-in ARC at the language level for 
classes. Interfaces are not only slow but then you need to pass around a 
reference to the interface instead of the class you actually care about. It 
makes no sense to me whatsoever.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Ryan Joseph via fpc-pascal


> On Feb 15, 2021, at 11:41 PM, Sven Barth via fpc-pascal 
>  wrote:
> 
> Again, the point of the interface would be to control which methods and 
> properties of the delegated  type would be available. Also it would allow for 
> seamless integration with the RTTI as it's possible to query the implemented 
> interface of a class and to retrieve a reference to it, thus it would be 
> possible to access this at runtime as well.
> 
> The compiler will generate a VMT for the interface with method thunks that 
> adjust the Self pointer so that it works correctly. The GetInterfaceByEntry 
> function uses this static data to generate an adjusted Self value that 
> callers can use.

As far as the "default implements property" is concerned we don't actually 
need, or even want to utilize the interfaces themselves. The idea of leveraging 
the interface syntax for exporting method names is one thing but if we actually 
have to use the interfaces internally then we open a whole can of worms in 
terms of performance.  Class instantiation may be taking on some significant 
baggage now also as the compiler sets up these interface VMT tables, even if 
the user never needs or wants them.

For example this code should be a compile time indirection instead of a hidden 
interface cast, right? The method forwarding syntax which works by modifying 
the VMT can hopefully be done at compile time for the default property also. 

type
  ICircle = interface
procedure Draw;
  end;

type
  TCircle = record
procedure Draw;
  end;

type
  TMyShape = class(ICircle)
private
  FCircle: TCircle;
public
  property Circle: TCircle read FCircle implements ICircle; default;
  end;

var
  shape: TMyShape;
begin
  shape := TMyShape.Create;
  { calls shape.FCircle.Draw instead of ICircle(shape).Draw }
  shape.Draw;
end.


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-16 Thread Benito van der Zander via fpc-pascal




There are no significant performance implications of interfaces. 
They're essentially a virtual method call, something that one is doing 
all day long with Object Pascal classes. 



Interfaces are extremely slow. Virtual method calls are also slow. I 
have been using interfaces for reference counting, and have been 
thinking to replace it all with managed records because they are so slow 
(unfortunately records can also be slow because fpc does not always keep 
them in registers even if they are pointer sized)


There we have:

* slower creation of the class, because each implemented interface adds 
another VMT reference to the class which needs to be initialized.


* slower method calling because of the wrapper function

* extremely slow casting an interface to the class. Never cast an 
interface, better add a method to the interface returning a class reference


* slow reference counting. Especially if it is thread safe and exception 
safe with the implicit exception block



On 14/02/2021 11:31, Sven Barth via fpc-pascal wrote:

Am 14.02.2021 um 01:09 schrieb Ben Grasset via fpc-pascal:
This seems possibly a *little* too similar to the existing Interface 
type in Object Pascal, however, I *would* really like to see some 
kind of functionality that basically amounts to "has the same 
capabilities as Interfaces and works on records and objects too, but 
does NOT require any kind of heap allocation".


My personal idea for this would be to allow for duck typing regarding 
interfaces: as long as the methods of the class, object or record 
satisfy those of the interface then it's possible to assign it. In 
case of objects and records one would mainly use raw interfaces (aka 
corba ones) instead of reference counted one as otherwise one would 
need to provide the methods of IInterface as well though one might not 
be able to implement them in a usable way as stack objects simply 
don't work that way.


So whether it be this, or just an improvement on the Interfaces we 
already have, I'd definitely personally be in favor of something that 
"works like Interfaces except minus the negative performance 
implications."


There are no significant performance implications of interfaces. 
They're essentially a virtual method call, something that one is doing 
all day long with Object Pascal classes.


Regards,
Sven
___
fpc-pascal maillist  - fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 19:22 schrieb Ryan Joseph via fpc-pascal:



On Feb 14, 2021, at 11:14 AM, Sven Barth via fpc-pascal 
 wrote:

It's a runtime conversion, but normally you don't do the conversion for every 
method call, but instead cache the interface (or work solely with the 
interface), thus the performance impact through the conversion amortizes (and 
again, there's so memory allocation by the conversion itself, of course if the 
interface getter in case of a interface delegation does a lazy initialization 
that's different, but the conversion itself does not require an allocation).

For default properties no conversion needs to take place even because the compiler can 
simply find the correct method and return a modified self which points to the struct, 
like "self.fMyRec" in the example below. In this case the interface doesn't 
really do anything does it? I think the VMT table is modified and you should probably use 
CORBA interface unless you want reference counting and extra baggage when you instantiate 
the class (I assume). If we use records then the memory should be unified (which is good) 
and with CORBA interfaces no significant baggage during instantiation. Correct me where 
I'm wrong please.


Again, the point of the interface would be to control which methods and 
properties of the delegated  type would be available. Also it would 
allow for seamless integration with the RTTI as it's possible to query 
the implemented interface of a class and to retrieve a reference to it, 
thus it would be possible to access this at runtime as well.


The compiler will generate a VMT for the interface with method thunks 
that adjust the Self pointer so that it works correctly. The 
GetInterfaceByEntry function uses this static data to generate an 
adjusted Self value that callers can use.


Also for the difference between COM and raw interfaces the only real 
difference is that a call to _AddRef will be inserted. But I agree that 
it should also work with raw interfaces (if it doesn't already).


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Sven Barth via fpc-pascal

Am 15.02.2021 um 18:08 schrieb Ryan Joseph via fpc-pascal:



On Feb 15, 2021, at 9:08 AM, Marcos Douglas B. Santos via fpc-pascal 
 wrote:

As I understand, this is not a method hiding, but just to tell the
compiler which method to implement the interface—the interface could
have method names which are very very common to use and this syntax
allows us to "rename" those methods without changing the original
names that the class might have.

yeah you may be right, that the interface object itself won't be hookup 
correctly if this doesn't happen. Seems strange you have 2 declarations for the 
same method though.

   procedure IMyIntf.Test2 = MyTest2;
   propcedure MyTest2;


instead of something like:
   
   propcedure MyTest2 override  IMyIntf.Test2;


Because the first one is much more powerful. As Marco said, you can 
reference a method in the parent class. Also you can redirect different 
interface methods to the same method:


=== code begin ===

type
  IMyIntf1 = interface
    procedure Foo;
  end;

  IMyIntf2 = interface
    procedure Bar;
  end;

  TMyClass = class(TInterfacedObject, IMyIntf1, IMyIntf2)
  public
    procedure IMyIntf1.Foo = Foobar;
    procedure IMyIntf2.Bar = Foobar;

    procedure Foobar;
  end;

=== code end ===

Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Sven Barth via fpc-pascal

Am 15.02.2021 um 16:52 schrieb Ryan Joseph via fpc-pascal:



On Feb 14, 2021, at 11:43 PM, Sven Barth via fpc-pascal 
 wrote:

Same names should be rejected. Providing a new implementation can be controlled 
using the interface alias:

=== code begin ===

type
   TMyClass = class(TInterfacedObject, IMyIntf)
   private
 fMyRecord: TMyRecord;
   public
 procedure IMyIntf.Test2 = MyTest2;
 // this will hoist all methods except Test2 from TMyRecord and MyTest2 as 
Test2 from the class itself
 property MyRecord: TMyRecord read fMyRecord implements IMyIntf; default;
 propcedure MyTest2;
   end;

So "procedure IMyIntf.Test2 = MyTest2;" basically tells the compiler to ignore 
the duplicate name resections and then when it encounters the MyTest2 there is no error?


No, it tells the compiler to fullfill the IMyIntf.Test2 requirement by 
using TMyClass.MyTest2 instead of TMyRecord.Test2. This way you can 
control which methods are from the delegate and which might be specific 
to the container class. It also allows to differentiate if you have two 
interfaces with the same method:


=== code begin ===

type
  IMyIntf1 = interface
    procedure Test;
  end;

  IMyIntf2 = interface
    procedure Test;
  end;

  TMyClass = class(TInterfacedObject, IMyIntf1, IMyIntf2)
  public
    procedure IMyIntf1.Test = Foobar;
    procedure Test; // for IMyIntf2
    procedure Foobar; // for IMyIntf1
  end;

=== code end ===



A duplicate name in this context is basically just method hiding (like in 
normal OOP) so you could argue it should be allowed. If the property came 
before then the interface would hide the local declaration. Not too different 
from how normal inheritance works really.


It should only hide if the method in question is from a parent class. 
For the declarations in the same class there should be errors, cause the 
implementor should know better than to introduce potentially ambigious 
situations.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Marco van de Voort via fpc-pascal


Op 2021-02-15 om 18:08 schreef Ryan Joseph via fpc-pascal:



As I understand, this is not a method hiding, but just to tell the
compiler which method to implement the interface—the interface could
have method names which are very very common to use and this syntax
allows us to "rename" those methods without changing the original
names that the class might have.

yeah you may be right, that the interface object itself won't be hookup 
correctly if this doesn't happen. Seems strange you have 2 declarations for the 
same method though.

   procedure IMyIntf.Test2 = MyTest2;
   propcedure MyTest2;


instead of something like:
   
   propcedure MyTest2 override  IMyIntf.Test2;

The first syntax allows to for methods to be declared in the ancestor too.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Ryan Joseph via fpc-pascal


> On Feb 15, 2021, at 9:08 AM, Marcos Douglas B. Santos via fpc-pascal 
>  wrote:
> 
> As I understand, this is not a method hiding, but just to tell the
> compiler which method to implement the interface—the interface could
> have method names which are very very common to use and this syntax
> allows us to "rename" those methods without changing the original
> names that the class might have.

yeah you may be right, that the interface object itself won't be hookup 
correctly if this doesn't happen. Seems strange you have 2 declarations for the 
same method though.

  procedure IMyIntf.Test2 = MyTest2;
  propcedure MyTest2;


instead of something like:
  
  propcedure MyTest2 override  IMyIntf.Test2;


Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Marcos Douglas B. Santos via fpc-pascal
On Mon, Feb 15, 2021 at 12:52 PM Ryan Joseph via fpc-pascal
 wrote:
>
> > On Feb 14, 2021, at 11:43 PM, Sven Barth via fpc-pascal 
> >  wrote:
> >
> > Same names should be rejected. Providing a new implementation can be 
> > controlled using the interface alias:
> >
> > === code begin ===
> >
> > type
> >   TMyClass = class(TInterfacedObject, IMyIntf)
> >   private
> > fMyRecord: TMyRecord;
> >   public
> > procedure IMyIntf.Test2 = MyTest2;
> > // this will hoist all methods except Test2 from TMyRecord and MyTest2 
> > as Test2 from the class itself
> > property MyRecord: TMyRecord read fMyRecord implements IMyIntf; default;
> > propcedure MyTest2;
> >   end;
>
> So "procedure IMyIntf.Test2 = MyTest2;" basically tells the compiler to 
> ignore the duplicate name resections and then when it encounters the MyTest2 
> there is no error?
>
> A duplicate name in this context is basically just method hiding (like in 
> normal OOP) so you could argue it should be allowed. If the property came 
> before then the interface would hide the local declaration. Not too different 
> from how normal inheritance works really.
>

As I understand, this is not a method hiding, but just to tell the
compiler which method to implement the interface—the interface could
have method names which are very very common to use and this syntax
allows us to "rename" those methods without changing the original
names that the class might have.

Marcos Douglas
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-15 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 11:43 PM, Sven Barth via fpc-pascal 
>  wrote:
> 
> Same names should be rejected. Providing a new implementation can be 
> controlled using the interface alias:
> 
> === code begin ===
> 
> type
>   TMyClass = class(TInterfacedObject, IMyIntf)
>   private
> fMyRecord: TMyRecord;
>   public
> procedure IMyIntf.Test2 = MyTest2;
> // this will hoist all methods except Test2 from TMyRecord and MyTest2 as 
> Test2 from the class itself
> property MyRecord: TMyRecord read fMyRecord implements IMyIntf; default;
> propcedure MyTest2;
>   end;

So "procedure IMyIntf.Test2 = MyTest2;" basically tells the compiler to ignore 
the duplicate name resections and then when it encounters the MyTest2 there is 
no error?

A duplicate name in this context is basically just method hiding (like in 
normal OOP) so you could argue it should be allowed. If the property came 
before then the interface would hide the local declaration. Not too different 
from how normal inheritance works really.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 19:03 schrieb Ryan Joseph via fpc-pascal:



What it leaves desired:

1) Making a dummy interface is annoying boiler plate but not a deal breaker.

Again, I see this is part of Pascal's declarativeness. Also this way you can 
make sure that only those methods/properties that you need are hoisted into the 
parent object allowing for easier reuse of existing classes/records/objects.

That is certainly true. With the idea of "default record properties"/traits the 
visibility sections like public would have to be used to define the exportable members. The 
"declarativeness" is also boiler plate but I do see your point. Not a deal breaker though 
but it may get tedious if you used this feature often.


One can always think about relaxations/improvements later on. As said 
before: let's get the foundation down right, first.



Another question, what does overriding mean in this context of interface delegation? With 
"default implements" it would be possible to add a method with the same name, 
which should be an error but it opens a possibility to provide a new method 
implementation.


Same names should be rejected. Providing a new implementation can be 
controlled using the interface alias:


=== code begin ===

type
  TMyClass = class(TInterfacedObject, IMyIntf)
  private
    fMyRecord: TMyRecord;
  public
    procedure IMyIntf.Test2 = MyTest2;
    // this will hoist all methods except Test2 from TMyRecord and 
MyTest2 as Test2 from the class itself
    property MyRecord: TMyRecord read fMyRecord implements IMyIntf; 
default;

    propcedure MyTest2;
  end;

=== code end ===

Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Marco van de Voort via fpc-pascal


Op 2021-02-14 om 17:16 schreef Michael Van Canneyt via fpc-pascal:


except it doesn't require an interface. The thing we seem to want 
here is a default property which works on records/objects so maybe we 
can just focus on that? The interface has so much boiler plate and I 
don't really see what we're getting in return.


I didn't propose using an interface; For me the

property Test: TTestImpl read fTest implements TTestIimpl;

Where TTestImpl is an object (tp style) or advanced record (delphi 
style) is ample.



You mean in both cases this will lead to a deep copy ? :-)
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Marco van de Voort via fpc-pascal


Op 2021-02-14 om 13:30 schreef Michael Van Canneyt via fpc-pascal:
I think the "performance implications" is more referring to memory 
management.


Interfaces means an extra object per interface, so more objects on the 
heap with all the extra memory management that implies.


(not interfaces do, as they are just a contract, but delegation of 
interfaces does)



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 11:14 AM, Sven Barth via fpc-pascal 
>  wrote:
> 
> It's a runtime conversion, but normally you don't do the conversion for every 
> method call, but instead cache the interface (or work solely with the 
> interface), thus the performance impact through the conversion amortizes (and 
> again, there's so memory allocation by the conversion itself, of course if 
> the interface getter in case of a interface delegation does a lazy 
> initialization that's different, but the conversion itself does not require 
> an allocation).

For default properties no conversion needs to take place even because the 
compiler can simply find the correct method and return a modified self which 
points to the struct, like "self.fMyRec" in the example below. In this case the 
interface doesn't really do anything does it? I think the VMT table is modified 
and you should probably use CORBA interface unless you want reference counting 
and extra baggage when you instantiate the class (I assume). If we use records 
then the memory should be unified (which is good) and with CORBA interfaces no 
significant baggage during instantiation. Correct me where I'm wrong please.

=

type
  TMyRec = record
procedure DoX;
  end;

  IMyIntf = interface
procedure DoX;
  end;

  TMyClass = class(TInterfacedObject, IMyIntf)
  private
fMyRec: TMyRec;
  public
property MyRec: TMyRec read fMyRec implements IMyIntf; default;
  end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 19:07 schrieb Ryan Joseph via fpc-pascal:



On Feb 14, 2021, at 9:55 AM, Sven Barth via fpc-pascal 
 wrote:

No, interfaces by *themselves* don't mean that. An interface instance is merely 
a shifted Self pointer that points to the static VMT of which the entries 
correct the Self pointer again upon the method call. This can be seen in 
GetInterfaceByEntry in rtl/inc/objpas.inc (the core function dealing with the 
casting of class to interface).

Remind me again, what happens when you cast a class to an interface? It's a 
runtime check with GetInterfaceByEntry?
It's a runtime conversion, but normally you don't do the conversion for 
every method call, but instead cache the interface (or work solely with 
the interface), thus the performance impact through the conversion 
amortizes (and again, there's so memory allocation by the conversion 
itself, of course if the interface getter in case of a interface 
delegation does a lazy initialization that's different, but the 
conversion itself does not require an allocation).


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 9:55 AM, Sven Barth via fpc-pascal 
>  wrote:
> 
> No, interfaces by *themselves* don't mean that. An interface instance is 
> merely a shifted Self pointer that points to the static VMT of which the 
> entries correct the Self pointer again upon the method call. This can be seen 
> in GetInterfaceByEntry in rtl/inc/objpas.inc (the core function dealing with 
> the casting of class to interface).

Remind me again, what happens when you cast a class to an interface? It's a 
runtime check with GetInterfaceByEntry?

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 10:20 AM, Sven Barth via fpc-pascal 
>  wrote:
> 
> To be fair, that *is* how inheritance works for example if you try to use OOP 
> in C... (though it would be more like "sphere.parent.parent.Draw()")

and that was bad. :) in PHP you're forced to use this-> instead of implicit 
self and even that is annoying.

> 
>> What it leaves desired:
>> 
>> 1) Making a dummy interface is annoying boiler plate but not a deal breaker.
> 
> Again, I see this is part of Pascal's declarativeness. Also this way you can 
> make sure that only those methods/properties that you need are hoisted into 
> the parent object allowing for easier reuse of existing 
> classes/records/objects.

That is certainly true. With the idea of "default record properties"/traits the 
visibility sections like public would have to be used to define the exportable 
members. The "declarativeness" is also boiler plate but I do see your point. 
Not a deal breaker though but it may get tedious if you used this feature often.

>> 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.
> 
> One can explore further improvements down the road (e.g. fields), but we 
> should provide a solid base first.

Yes, you're right properties do work but require the getter/settings methods.

Another question, what does overriding mean in this context of interface 
delegation? With "default implements" it would be possible to add a method with 
the same name, which should be an error but it opens a possibility to provide a 
new method implementation.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Martin Frb via fpc-pascal

On 14/02/2021 18:20, Sven Barth via fpc-pascal wrote:




   TTest = class(TObject, ITest)
   private
 fTest: TTestImpl;
   public
 property Test: TTestImpl read fTest implements ITest; default;
   end;


Well there is a difference there.

Interfaces are essentially inheritance. One can access TTest via "TTest 
as ITest".


A trait (in any of the proposed forms) would not allow that. (Hence a 
trait should probably also not be it the  TFoo=class(...) part).


So that makes 2 differences for trait vs interface
- The result of the composition can not be cast to either of the parts 
making it up

  That is a functional difference
- Traits remove the need to declare all methods twice (once in the 
interface, once in the class (or whatever implementing struct))
  That can be considered syntactic sugar (though it might have a quite 
impacting effect)



Further, IIRC it was said that:
For interfaces the syntax is already fully available. But some features 
are not yet supported by fpc.

Though Delphi has them, so the might come anyway.
However for traits as "default property" the syntax also already exists.

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 02:45 schrieb Ryan Joseph via fpc-pascal:

On Feb 13, 2021, at 12:38 PM, Sven Barth  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


Wrong, properties are supported as I showed, though they need to be with 
getters and setters right now.


And supporting the hoisting of class operators would be *wrong*, because 
they could only work on the delegated type as it would not know about 
the parent class.



3) Requires you to define an interface with duplicate methods, so boilerplate 
code


One can argue whether this is really boilerplate code or just an 
expression of Pascal's declarativeness (also see below).



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


To be fair, that *is* how inheritance works for example if you try to 
use OOP in C... (though it would be more like "sphere.parent.parent.Draw()")



What it leaves desired:

1) Making a dummy interface is annoying boiler plate but not a deal breaker.


Again, I see this is part of Pascal's declarativeness. Also this way you 
can make sure that only those methods/properties that you need are 
hoisted into the parent object allowing for easier reuse of existing 
classes/records/objects.


Take this for example:

=== code begin ===

type
  TMyRec = record
    procedure DoX;
    procedure DoY;
  end;

  IMyIntf = interface
    procedure DoX;
  end;

  TMyClass = class(TInterfacedObject, IMyIntf)
  private
    fMyRec: TMyRec;
  public
    property MyRec: TMyRec read fMyRec implements IMyIntf; default;
  end;

var
  t: TMyClass;
begin
  t := TMyClass.Create;
  try
    t.DoX;
    // this won't work however
    //t.DoY;
  finally
    t.Free;
  end;
end.

=== code end ===

Also it provides you with the possibility to really only pass on the 
interface to something that only expects the interface (though with the 
introduction of duck typed interfaces that wouldn't be that much of a 
problem either, albeit here the mapping to the interface would be 
determined at the time of the declaration of the class instead of when 
the cast happens)



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.


No class operators. As mentioned above they wouldn't work anyway.

Properties *are* supported though they need getters and setters.


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.


One can explore further improvements down the road (e.g. fields), but we 
should provide a solid base first.





Of course this does not provide any

Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Michael Van Canneyt via fpc-pascal



On Sun, 14 Feb 2021, Sven Barth via fpc-pascal wrote:


Am 14.02.2021 um 13:30 schrieb Michael Van Canneyt via fpc-pascal:



On Sun, 14 Feb 2021, Sven Barth via fpc-pascal wrote:

So whether it be this, or just an improvement on the Interfaces we 
already have, I'd definitely personally be in favor of something 
that "works like Interfaces except minus the negative performance 
implications."


There are no significant performance implications of interfaces. 
They're essentially a virtual method call, something that one is 
doing all day long with Object Pascal classes.


I think the "performance implications" is more referring to memory 
management.


Interfaces means an extra object per interface, so more objects on the 
heap with all the extra memory management that implies.


No, interfaces by *themselves* don't mean that.


I meant in the context of traits and mixins, obviously.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 13:30 schrieb Michael Van Canneyt via fpc-pascal:



On Sun, 14 Feb 2021, Sven Barth via fpc-pascal wrote:

So whether it be this, or just an improvement on the Interfaces we 
already have, I'd definitely personally be in favor of something 
that "works like Interfaces except minus the negative performance 
implications."


There are no significant performance implications of interfaces. 
They're essentially a virtual method call, something that one is 
doing all day long with Object Pascal classes.


I think the "performance implications" is more referring to memory 
management.


Interfaces means an extra object per interface, so more objects on the 
heap with all the extra memory management that implies.


No, interfaces by *themselves* don't mean that. An interface instance is 
merely a shifted Self pointer that points to the static VMT of which the 
entries correct the Self pointer again upon the method call. This can be 
seen in GetInterfaceByEntry in rtl/inc/objpas.inc (the core function 
dealing with the casting of class to interface).


That is why I think that a 'trait' using an object/advanced record has 
it's advantage: There is only 1 continuous memory block for a class 
implementing

traits.


For delegation it is true that memory allocation is involved, but let's 
be fair here: this instance is normally allocated together with its 
parent instance and thus the performance impact only adds to the already 
costly instantiation of the parent instance. And if you really need to 
worry about heap fragmentation then you have other problems in my opinion...


That said, as I wrote, one could in principle allow the use of records 
and objects for the delegation as well, after all delegation classes 
don't need to implement the interface either and the compiler needs to 
create appropriate thunks anyway.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 9:16 AM, Michael Van Canneyt via fpc-pascal 
>  wrote:
> 
> I didn't propose using an interface; For me the
> 
> property Test: TTestImpl read fTest implements TTestIimpl;
> 
> Where TTestImpl is an object (tp style) or advanced record (delphi style) is 
> ample.

That was directly more at Sven. I like the idea of a "default property" and 
Sven appears to agree, but there's some debate on how this should be achieved. 
In fact we already had another long thread on default properties some years ago 
but it got derailed due to the implication of using default properties with all 
types.

Maybe what we really want is a default property for struct types which does 
strict name collision checking?

property Test: TSomeRec read fTest; default;

That's a simple idea which is easy to reason about, doesn't require any new 
syntax and is easy to implement (more on that if we get that far).

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Michael Van Canneyt via fpc-pascal



On Sun, 14 Feb 2021, Ryan Joseph via fpc-pascal wrote:





On Feb 14, 2021, at 5:30 AM, Michael Van Canneyt via fpc-pascal 
 wrote:

Interfaces means an extra object per interface, so more objects on the heap 
with all the extra memory management that implies.


You mean COM interfaces? I've only ever used CORBA style ones which I thought 
were just contracts basically. The supports() function returns a pointer to 
something which I thought was part of the existing VMT table.



That is why I think that a 'trait' using an object/advanced record has it's 
advantage: There is only 1 continuous memory block for a class implementing
traits.


Considering the functionality we actually want, i.e. something like inheritance, you could achieve 
the trait by simply making an "imports" property. No need to even introduce the extra 
"trait" type. For example:

 property Test: TTestImpl import FTest;

which is basically the same as:

 property Test: TTestImpl read fTest implements ITest; default;

except it doesn't require an interface. The thing we seem to want here is a 
default property which works on records/objects so maybe we can just focus on 
that? The interface has so much boiler plate and I don't really see what we're 
getting in return.


I didn't propose using an interface; For me the

property Test: TTestImpl read fTest implements TTestIimpl;

Where TTestImpl is an object (tp style) or advanced record (delphi style) is 
ample.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Ryan Joseph via fpc-pascal


> On Feb 14, 2021, at 5:30 AM, Michael Van Canneyt via fpc-pascal 
>  wrote:
> 
> Interfaces means an extra object per interface, so more objects on the heap 
> with all the extra memory management that implies.

You mean COM interfaces? I've only ever used CORBA style ones which I thought 
were just contracts basically. The supports() function returns a pointer to 
something which I thought was part of the existing VMT table.

> 
> That is why I think that a 'trait' using an object/advanced record has it's 
> advantage: There is only 1 continuous memory block for a class implementing
> traits.

Considering the functionality we actually want, i.e. something like 
inheritance, you could achieve the trait by simply making an "imports" 
property. No need to even introduce the extra "trait" type. For example:

  property Test: TTestImpl import FTest;

which is basically the same as:

  property Test: TTestImpl read fTest implements ITest; default;

except it doesn't require an interface. The thing we seem to want here is a 
default property which works on records/objects so maybe we can just focus on 
that? The interface has so much boiler plate and I don't really see what we're 
getting in return.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Michael Van Canneyt via fpc-pascal



On Sun, 14 Feb 2021, Sven Barth via fpc-pascal wrote:

So whether it be this, or just an improvement on the Interfaces we 
already have, I'd definitely personally be in favor of something that 
"works like Interfaces except minus the negative performance 
implications."


There are no significant performance implications of interfaces. They're 
essentially a virtual method call, something that one is doing all day 
long with Object Pascal classes.


I think the "performance implications" is more referring to memory management.

Interfaces means an extra object per interface, so more objects on the 
heap with all the extra memory management that implies.


That is why I think that a 'trait' using an object/advanced record has it's 
advantage: There is only 1 continuous memory block for a class implementing

traits.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-14 Thread Sven Barth via fpc-pascal

Am 14.02.2021 um 01:09 schrieb Ben Grasset via fpc-pascal:
This seems possibly a *little* too similar to the existing Interface 
type in Object Pascal, however, I *would* really like to see some kind 
of functionality that basically amounts to "has the same capabilities 
as Interfaces and works on records and objects too, but does NOT 
require any kind of heap allocation".


My personal idea for this would be to allow for duck typing regarding 
interfaces: as long as the methods of the class, object or record 
satisfy those of the interface then it's possible to assign it. In case 
of objects and records one would mainly use raw interfaces (aka corba 
ones) instead of reference counted one as otherwise one would need to 
provide the methods of IInterface as well though one might not be able 
to implement them in a usable way as stack objects simply don't work 
that way.


So whether it be this, or just an improvement on the Interfaces we 
already have, I'd definitely personally be in favor of something that 
"works like Interfaces except minus the negative performance 
implications."


There are no significant performance implications of interfaces. They're 
essentially a virtual method call, something that one is doing all day 
long with Object Pascal classes.


Regards,
Sven
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-13 Thread Ryan Joseph via fpc-pascal


> On Feb 13, 2021, at 5:09 PM, Ben Grasset via fpc-pascal 
>  wrote:
> 
> This seems possibly a *little* too similar to the existing Interface type in 
> Object Pascal, however, I *would* really like to see some kind of 
> functionality that basically amounts to "has the same capabilities as 
> Interfaces and works on records and objects too, but does NOT require any 
> kind of heap allocation".

Not really, traits/mixins are multiple inheritance alternative.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-13 Thread Ryan Joseph via fpc-pascal

> On Feb 13, 2021, at 12:38 PM, Sven Barth  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

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-13 Thread Ben Grasset via fpc-pascal
This seems possibly a *little* too similar to the existing Interface type
in Object Pascal, however, I *would* really like to see some kind of
functionality that basically amounts to "has the same capabilities as
Interfaces and works on records and objects too, but does NOT require any
kind of heap allocation".

So whether it be this, or just an improvement on the Interfaces we already
have, I'd definitely personally be in favor of something that "works like
Interfaces except minus the negative performance implications."

On Tue, Feb 9, 2021 at 9:27 PM Ryan Joseph via fpc-pascal <
fpc-pascal@lists.freepascal.org> wrote:

> We had talked about this some time ago and it's been rattling around in my
> brain so I wanted to write it down into a formal proposal where we can
> discuss it and hopefully agree upon a syntax. Everything is preliminary and
> tentative but this is a syntax which allows a "composition over
> inheritance" model, also known as "mix-ins" in some languages. That idea is
> similar to multiple inheritance except you have a concrete reference to the
> trait being implemented so you can resolve conflicts easily.
>
> Here's what I have so far. Please feel free to look at it and give any
> feedback.
>
> https://github.com/genericptr/freepascal/wiki/Traits-Proposal
>
> 
>
> type
>   TSomeTrait = trait
> public
>   field: integer;
>   procedure DoThis;
>   end;
>
>   TMyClass = class
> private
>   _trait: TSomeTrait;
> public
>   property someTrait: TSomeTrait implements _trait;
>   end;
>
> var
>   c: TMyClass;
> begin
>   c := TMyClass.Create;
>   c.DoThis;
>   writeln(c.field):
> end;
>
> Regards,
> Ryan Joseph
>
> ___
> fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
> https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
>
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-13 Thread gabor via fpc-pascal

W dniu 2021-02-13 o 20:38, Sven Barth via fpc-pascal pisze:
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.


So the complement could be generic classes like this:

type
  generic TTestGeneric = class(, ITest, IInterface2)
  private
fTest: TTestImpl;
  public
property Test: TTestImpl read fTest implements ITest; default;
  end;

  TMyTest = specialize TTestGeneric();

Something similar has already been asked:
https://www.mail-archive.com/fpc-pascal@lists.freepascal.org/msg53599.html

Michał.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-13 Thread Sven Barth via fpc-pascal

Am 10.02.2021 um 03:18 schrieb Ryan Joseph via fpc-pascal:

We had talked about this some time ago and it's been rattling around in my brain so I wanted to 
write it down into a formal proposal where we can discuss it and hopefully agree upon a syntax. 
Everything is preliminary and tentative but this is a syntax which allows a "composition over 
inheritance" model, also known as "mix-ins" in some languages. That idea is similar 
to multiple inheritance except you have a concrete reference to the trait being implemented so you 
can resolve conflicts easily.

Here's what I have so far. Please feel free to look at it and give any feedback.

https://github.com/genericptr/freepascal/wiki/Traits-Proposal


Time for me to tackle this topic as well. First of, I might incorporate 
answers to other mails of this thread here (directly or indirectly), 
just so you know.


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:


Allow the extension of a class' declaration with preexisting behavior 
AND state through the use of composition instead of inheritance, 
providing this in a convenient and possible even transparent way to the 
user of the class while allowing this to be implemented conveniently by 
the author of the class.


Let's break this down and let's see whether we can naturally evolve 
existing language features to cover this instead of trying to shoehorn 
concepts of other languages for this. Obviously I know where I'm going 
with this, but bear with me.


So the first part: "Allow the extension of a class' declaration with 
preexisting behavior AND state through the use of composition instead of 
inheritance". Obviously this is already possible in Object Pascal, 
namely by using the interface delegation feature. For those that aren't 
aware of the full capabilities of this feature, here is a small 
excursion (if you think you already know all there is to know, jump 
ahead to "end of the delegate excursion"):


The interface delegation feature allows the developer of a class to 
redirect the implementation of a specific interface to a class or 
interface instance provided in the class. A simple case looks like this:


=== code begin ===

type
  ITest = interface
    procedure Test;
  end;

  TTest = class(TObject, ITest)
  private
    fTest: ITest;
  public
    property TestImpl: ITest read fTest implements ITest;
  end;

=== code end ===

This is however not the full ability of this feature. The property 
implementing this interface does not need to be an interface. It can 
also be a class instance and this class instance does *not* need to 
declare that it implements this interface. So the following is a valid 
interface delegation as well (Note: FPC does not support this currently, 
that is still an outstanding Delphi compatibility problem):


=== code begin ===

type
  ITest = interface
    procedure Test;
  end;

  TTestImpl = class
    procedure Test;
  end;

  TTest = class(TInterfacedObject, ITest)
  private
    fTest: TTestImpl;
  public
    property TestImpl: TTestImpl read fTest implements ITest;
  end;

=== code end ===

In this case the methods introduced by IInterface are implemented by 
TTest's parent class and ITest.Test is implemented by the TTestImpl 
instance. If TTestImpl inherits from e.g. TInterfacedObject then TTest 
does not need to inherit from TInterfacedObject itself (Note: the 
reference counting in relation to interface delegation and aggregation 
is a whole topic in and of itself and will be ignored here).


One can even control which methods should be implemented by the property:

=== code begin ===

type
  ITest = interface
    procedure Test1;
    procedure Test2;
  end;

  TTestImpl = class
    procedure Test1;
  end;

  TTest = class(TInterfacedObject, ITest)
  private
    fTest: TTestImpl;
  public
    procedure ITest.Test2 = MyTest2; // name can be chosen freely, can 
also be "Test2"

    property TestImpl: TTestImpl read fTest implements ITest;
    procedure MyTest2;
  end;

=== code end ===

So far this only covered behavior, but as interfaces can also provide 
properties it's also possible to provide state through them (though they 
*have* to be implemented through explicit Getters and Setters):


=== code begin ===

type
  ITest = interface
    function GetTestProp: LongInt;
    procedure SetTestProp(aValue: LongInt);
    property TestProp: LongInt read GetTestProp write SetTestProp;
  end;

  TTestImpl = class
    function GetTestProp: LongInt;
    procedure SetTestProp(aValue: LongInt);
  end;

  TTest = class(TInterfacedObject, ITest)
  private
    fTest: TTestImpl;
  public
    property TestImpl: TTestImpl read fTest implements ITest;
  end;


=== code end ===

For using a delegated interface you need to cast

Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal

On 11/02/2021 04:13, Ryan Joseph via fpc-pascal wrote:



On Feb 10, 2021, at 7:47 PM, Martin Frb via fpc-pascal 
 wrote:

In that case IIRC, it was said traits are not allowed constructors. Why?

Traits are meant to be a way to import fields/methods and is based off of 
"object" (so it's on the stack). This is also important for using properties to 
alias the imported fields. It's the same with subclassing, you don't need to manually 
allocate the super class because it's all one structure with a shared memory space. The 
idea is to make a viable alternative to inheritance in the simplest way possible.

"don't need to manually allocate"
I know, I am using old "object" for that already. So that is not new 
when traits come.


Hence I concluded, the "trait" feature is exactly and only an automated 
generation of forwarder-methods (well exactly they are no longer 
forwarders, but imported / to the user that writes a call to them that 
is the same in the end)


But if the above conclusion is correct (no more, no less that replacing 
the need for forwarders ), then it is reasonable to want the same 
concept for other types... I do not actually advocate it. I simple point 
out what would logically follow (rather than what may or may not be good)



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 9:05 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
> Maybe Something like  (aliasing / no need to copy implementation)
> TDesignerList = trait(TEmployeList)
>   function  FindDesigerByUnallocateWorkTime: TEmploye; aliases 
> FindByUnallocateWorkTime;  // no body /just a name replacement
>   // and the others
> end;

I see. I used "override from" in the wiki. From my experience I don't think the 
compiler team will allow this since it adds news syntax and complexity so I 
suggest we focus on manual boilerplate functions for now. I will of course 
defer to them on these matters.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal

On 11/02/2021 04:20, Ryan Joseph via fpc-pascal wrote:



On Feb 10, 2021, at 7:47 PM, Martin Frb via fpc-pascal 
 wrote:

I understand it is for conflict resolution only. But see my example => as soon 
as you need to repeat a trait with just a change in name, you always need conflict 
resolution.

Please post the code snippet again. I'm not sure I understand what you're 
saying.


TEmployeList = trait
  function  FindByUnallocateWorkTime: TEmploye;
  // other functions/members
end;

TCompany = class
end;

Now the company could have Engineers and Designers. It needs to lists
But the trait can obviously only be added once.
Yet no-one wants to copy and paste the trait to create a 2nd verson

Maybe Something like  (aliasing / no need to copy implementation)
TDesignerList = trait(TEmployeList)
  function  FindDesigerByUnallocateWorkTime: TEmploye; aliases 
FindByUnallocateWorkTime;  // no body /just a name replacement

  // and the others
end;

===>
TCompany = class
  _TraitDesigner: TDesignerList ;
  _TraitEngeneers: TEmployeList;
end;


This is clearly something that can be done later. **IF** the design is 
chosen to allow for it.



___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 7:47 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
> I understand it is for conflict resolution only. But see my example => as 
> soon as you need to repeat a trait with just a change in name, you always 
> need conflict resolution.

Please post the code snippet again. I'm not sure I understand what you're 
saying. The most important thing here is that we don't allow the programmer to 
write ambiguous code. If all else fails the code must fail to compiler, if 
there's any chance of any conflict whatsoever. The first reason this idea gets 
shot down is because people think of crazy "with" statement bugs and we need to 
avoid that at all costs.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 7:47 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
> In that case IIRC, it was said traits are not allowed constructors. Why?

Traits are meant to be a way to import fields/methods and is based off of 
"object" (so it's on the stack). This is also important for using properties to 
alias the imported fields. It's the same with subclassing, you don't need to 
manually allocate the super class because it's all one structure with a shared 
memory space. The idea is to make a viable alternative to inheritance in the 
simplest way possible.

> 
>> procedure TMyClass.DoThis;
>> begin
>>   // resolve the conflict by directly referencing the trait
>>   traitA.DoThis;
>> end;
>> 
>> 
> This we already can do. Write our own forwarder method.
> I understand it is for conflict resolution only. But see my example => as 
> soon as you need to repeat a trait with just a change in name, you always 
> need conflict resolution.
> IMHO, there are many traits that a class could need more than once.

I know it's not the most elegant but we need to keep this simple if there's 
going to be any chance of it even being considered. I will let the compiler 
team defer to this.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal


On 11/02/2021 03:07, Ryan Joseph via fpc-pascal wrote:

We get the shared namespace
So basically, we get the compiler to automatically create forwarder 
methods for us (or at least that behaviour). And that is all there is. 
right?


In that case IIRC, it was said traits are not allowed constructors. Why?
Now a constructor would and should not have a forwarding method, but 
that the compiler can skip.

Yet, in my code, in the constructor of the TMyClass, I could add
  _traitA.Create
even
  _traitA := TTraitA.Create; // if the trait was not an object, but 
another class.


The compiler can with no doubt create the forwarder methods. And if I 
understand right, then that is all the compiler should do?

(Not that I advertise a trait should be a class / IMHO better not)



procedure TMyClass.DoThis;
begin
   // resolve the conflict by directly referencing the trait
   traitA.DoThis;
end;


This we already can do. Write our own forwarder method.
I understand it is for conflict resolution only. But see my example => 
as soon as you need to repeat a trait with just a change in name, you 
always need conflict resolution.

IMHO, there are many traits that a class could need more than once.

Also that only works, if a trait is included as a field.
There was at least one message in this mail-thread that suggested
  TMyClass = class(TObject, TTraitA);

The solution is on 
https://en.wikipedia.org/wiki/Trait_(computer_programming)
/alias/: an operation that creates a new trait by adding a new name 
for an existing method
Doing that for all methods, means having 2 identical traits, but with 
different names. => no conflict.




___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 10:17 AM, Martin Frb via fpc-pascal 
>  wrote:
> 
> - One might have to think about how to declare what a trait can access? 
>Just allow anything in the code, and attempt to resolve when the trait is 
> embedded? IMHO maybe not?

Sorry Martin you said a lot and I missed this. :) The plan is to only export 
fields/methods in the public section.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 2:42 PM, Ryan Joseph  wrote:
> 
>  We should keep it as simple as possible imo.

It's not as elegant as other ideas we may think of but I think it's good enough 
to let users write boilerplate methods to resolve naming conflicts. We don't 
need to introduce new syntax or concepts. This is the power of traits vs 
multiple inheritance. We get the shared namespace like normal OOP inheritance 
but have full control over any conflicts that arise by mixing in traits.



type
  TTraitA = trait
procedure DoThis;
  end;
  TTraitB = trait
procedure DoThis;
  end;
  
type
  TMyClass = class
private
  property traitA: TTraitA implements _traitA;
  property traitB: TTraitB implements _traitB;
public
  // DoThis now hides traitB.DoThis like it would in normal Object Pascal
  procedure DoThis;
  end;

procedure TMyClass.DoThis;
begin
  // resolve the conflict by directly referencing the trait
  traitA.DoThis;
end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 1:51 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
> I don't have a pro/contra agenda on whether that feature should be part of 
> the fpc trait or not.
> I just saw it, and thought I raise it.
> 

These issues you are raising are more related to general type 
safety/compatibility and probably beyond the scope of what traits should do. It 
also introduces lots of new concepts and syntax which make this exponentially 
more unlikely to get approved.

The only purpose of the trait is import fields/methods into a namespace of a 
struct, like multiple inheritance but with references. We should keep it as 
simple as possible imo.

> --
> Btw, next question that just came up
> 
> Will traits only apply to classes?
> 
> or could a record / advanced record / old object also receive traits ?
> 

I don't see why not but nothing is settled yet.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal

On 10/02/2021 21:17, Ryan Joseph via fpc-pascal wrote:



On Feb 10, 2021, at 12:40 PM, Martin Frb via fpc-pascal 
 wrote:

type
   TSomeTrait = trait
   public
 procedure DoTraitFoo;
   end;

   TSomeClass = class(TObject)
   private
 trait: TSomeTrait; // whatever syntax is used so that the trait is added
   public
 SomeVal: Integer;
 procedure DoSome;
 procedure Dispatch(var message); override;
   end;

if it's the simple then just set a reference to TSomeClass inside of 
TSomeTrait. If the trait doesn't know anything about the implementor then you 
indeed need to use a dispatch table or something. Can't this be solved with 
existing features?

btw, I don't think we should model this based on PHP, I just gave that as an 
example. The Swift model is much more simple and does 99% of what you want. I 
think that's what we should do for Pascal also.

I don't have a pro/contra agenda on whether that feature should be part 
of the fpc trait or not.

I just saw it, and thought I raise it.

But also, if any option should be reserved for the future, then it 
matters. Because re-using "sometrait = object end" makes it less likely 
that an "implements" clause will be added (because "object" does already 
have a fully defined syntax).

So I felled it might be worth being noted.

--
Another option would be

  TSomeTrait = trait (basetrait) for (someclass_as_minimum_base_class)

for (someclass) is of course  optional (or rather default to TObject).
So sometrait then can access
   self.any_trait_identifier
   self.someclass_ident

This is a bit limiting at first, but bring in genericts
  generic TSomeTrait = trait (basetrait) for ()
Since it is a generic the compiler will not complain about unknown 
identifiers.


It can automatically specialise, when applying a trait.

Then again the "implements" clause from above could internally create a 
generic too.


--
Btw, next question that just came up

Will traits only apply to classes?

or could a record / advanced record / old object also receive traits ?


And one more thing that may at least be considered for design in case of 
future use.


Consider a Trait
TEmployeList = trait
  function  FindByUnallocateWorkTime: TEmploye;
end;

TCompany = class
end;

Now the company could have Engineers and Designers. It needs to lists
But the trait can obviously only be added once.
Yet no-one wants to copy and paste the trait to create a 2nd verson

Maybe Something like
TDesignerList = trait(TEmployeList)
  function  FindDesigerByUnallocateWorkTime: TEmploye; aliases 
FindByUnallocateWorkTime;

end;

--
Again, none of those are needed. But if they are found interesting
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 12:40 PM, Martin Frb via fpc-pascal 
>  wrote:
> 
> type
>   TSomeTrait = trait 
>   public 
> procedure DoTraitFoo; 
>   end; 
> 
>   TSomeClass = class(TObject)
>   private
> trait: TSomeTrait; // whatever syntax is used so that the trait is added
>   public
> SomeVal: Integer;
> procedure DoSome;
> procedure Dispatch(var message); override;
>   end;

if it's the simple then just set a reference to TSomeClass inside of 
TSomeTrait. If the trait doesn't know anything about the implementor then you 
indeed need to use a dispatch table or something. Can't this be solved with 
existing features?

btw, I don't think we should model this based on PHP, I just gave that as an 
example. The Swift model is much more simple and does 99% of what you want. I 
think that's what we should do for Pascal also.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal

On 10/02/2021 20:17, Ryan Joseph via fpc-pascal wrote:



On Feb 10, 2021, at 11:09 AM, Ryan Joseph  wrote:

type
TSomeTrait = trait
   public
 parent: TObject;
 procedure DoThis;
end;

procedure TSomeTrait .DoThis;
begin
// ??? here is our issue. Is this good enough to call the TBaseClass.DoThis?
TBaseClass(parent).DoThis;
end;

Thinking about this more I don't think there's even a reason for it since Object Pascal 
doesn't let you do stuff like this anyways. If you want to call the super class you need 
to use "inherited" from within the class body.  The example I posted only works 
if there is no virtual/override involved.



Its not about the keyword to be used.
Its not even about the inheritance.

In the example from https://www.php.net/manual/en/language.oop5.traits.php
The trait accesses a method defined in the class to which the trait is 
applied. (It happens to be from the base class, but that does not matter)


In Pascal that would look like

type
  TSomeTrait = trait
  public
    procedure DoTraitFoo;
  end;

  TSomeClass = class(TObject)
  private
    trait: TSomeTrait; // whatever syntax is used so that the trait is 
added

  public
    SomeVal: Integer;
    procedure DoSome;
    procedure Dispatch(var message); override;
  end;

procedure TSomeTrait.DoTraitFoo;
begin
  inherited Dispatch(nil); // from TObject
  DispatchStr('');  // from TObject
  DoSome;  // from SomeClass
  SomeVal := 1;  // if we can access methods, then why not data
end;

In the example
|  parent::sayHello();|
"parent" does not refer to the class containing the trait. "parent" = 
"inherited"


The php example tait's sayHello hides the inherited sayHello => so 
parent is needed.


So in the example this/self of the embedded trait is the entire object 
into which it is embedded.


That means also: the trait and the class must be conflictfree even for  
private variables. (except where maybe cross unit scoping hides them???)


-
Leaving scoping/conflicts aside.

In Pascal TSomeTrait as it is given above can not compile, as it does 
not know what DoSome,SomeVal,Dispatch are. Not until it is embedded.

Well it could compile as a generic...

So, if a trait should have such access, then how to make sure the trait 
can compile?

Maybe:

  TSomeTrait = trait
  imports
// Those must be implement/provided by classes to which the trait is added.
    procedure DoSome;
    SomeVal : integer;
  public
    procedure DoTraitFoo;
  end;

Or (less flexible)
  TSomeTrait = trait
  extends(TSomeClass)  // can extend anything that is inherited from 
TSomeClass (i.e can access anything that a TSomeClass has)

  public
    procedure DoTraitFoo;
  end;
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 12:17 PM, Ryan Joseph  wrote:
> 
> Thinking about this more I don't think there's even a reason for it since 
> Object Pascal doesn't let you do stuff like this anyways. If you want to call 
> the super class you need to use "inherited" from within the class body.  The 
> example I posted only works if there is no virtual/override involved.

Also, PHP doesn't have function overloading so they need to add all these 
complicated conflict resolution syntaxes into their implementation. For Pascal 
we don't have this problem and we can simply rely on the existing rules. The 
only exception is when you override a method that is implemented as a trait 
(see the "Overrides" section in the wiki). We may not even want to allow this 
because it's an edge case that could be resolved by explicit references. Traits 
can be VERY simple if we keep it that way.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 11:09 AM, Ryan Joseph  wrote:
> 
> type
> TSomeTrait = trait
>   public
> parent: TObject;
> procedure DoThis;
> end; 
> 
> procedure TSomeTrait .DoThis;
> begin
>// ??? here is our issue. Is this good enough to call the 
> TBaseClass.DoThis?
>TBaseClass(parent).DoThis;
> end;

Thinking about this more I don't think there's even a reason for it since 
Object Pascal doesn't let you do stuff like this anyways. If you want to call 
the super class you need to use "inherited" from within the class body.  The 
example I posted only works if there is no virtual/override involved.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 10:17 AM, Martin Frb via fpc-pascal 
>  wrote:
> 
> The example exposes another aspect:
> 
>> trait SayWorld {
>> public function sayHello() {
>> parent::sayHello();
> 
> In this case the trait has access to the object/class into which it is 
> embedded. (otherwise it could not call the inherited method of the outer 
> class).
> 
> Is that wanted?
> (methods and fields?)
> 
> If that is wanted
> - If it will be using the existing "object" (or advanced record), then such 
> code is not possible.
> - One might have to think about how to declare what a trait can access? 
>Just allow anything in the code, and attempt to resolve when the trait is 
> embedded? IMHO maybe not?

Here's an example of how this could work. "parent" could in theory be a hidden 
field which the compiler sets for you when the class implementing the trait is 
instantiated. 

If there was no virtual/override then there would be no VMT and normal casting 
would work to call the base class method.

This is the area I'm not sure about yet, i.e. VMT table and overriding. There 
are some examples on the wiki also.



type
 TSomeTrait = trait
   public
 parent: TObject;
 procedure DoThis;
 end; 

 procedure TSomeTrait .DoThis;
 begin
// ??? here is our issue. Is this good enough to call the TBaseClass.DoThis?
TBaseClass(parent).DoThis;
 end;

 TBaseClass = class
procedure DoThis; virtual;
 end;

 TMyClass = class(TBaseClass)
   private
 _trait: TSomeTrait;
   public
 property someTrait: TSomeTrait implements _trait;
 // we need override because TBaseClass.DoThis is virtual.
 // The compiler can generate the body for us if we don't implement it.
 procedure DoThis; override;
 procedure AfterConstruction; override;
 end;

procedure TMyClass .AfterConstruction;
begin
  someTrait.parent := self;
end;

var
 c: TMyClass;
begin
 c := TMyClass.Create;
 c.DoThis;
end;

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Martin Frb via fpc-pascal

On 10/02/2021 16:59, Ryan Joseph via fpc-pascal wrote:

• PHP (trait): https://www.php.net/manual/en/language.oop5.traits.php



The example exposes another aspect:


|trait SayWorld {
public function sayHello() {
parent::sayHello();|


|In this case the trait has access to the object/class into which it is 
embedded. (otherwise it could not call the inherited method of the outer 
class).


Is that wanted?
(methods and fields?)

If that is wanted
- If it will be using the existing "object" (or advanced record), then 
such code is not possible.

- One might have to think about how to declare what a trait can access?
   Just allow anything in the code, and attempt to resolve when the 
trait is embedded? IMHO maybe not?


|
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 9:06 AM, Michael Van Canneyt  
> wrote:
> 
> You said it yourself: internally it will just be an object. Just make it 
> formally so.
> 
> I also think the argument of reusing existing objects deserves consideration.

Is it strange that there are extra things in the object which aren't related to 
the trait? You could declare destructors/constructors and maybe function 
modifiers that don't make sense in modern code. I don't have a strong argument 
I guess except it feels clean to start with a fresh slate. I'm also assuming 
that during development we'll find problems with how to handle feature X of 
objects which are conflicting somehow and it would be easier to have a new type 
to resolve this. I'll wait to hear from others.

Still, the biggest hurdle is getting any acceptance from the compiler team. I 
really don't like the interface delegation pattern (from Delphi?) because it 
doesn't help with namespaces but as Sven suggested this should be good enough 
for us. Traits are easy to understand and implement but the interface 
delegation requires tons of boiler plate and you still need to subscript into 
classes instead of getting a nice unified namespace.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Michael Van Canneyt via fpc-pascal



On Wed, 10 Feb 2021, Ryan Joseph via fpc-pascal wrote:





On Feb 10, 2021, at 8:41 AM, Michael Van Canneyt  wrote:

I heavily object to this; We have way too much keywords as it is already. So 
unless there really is no other way I don't think we should introduce
even more.


I guess the first question is whether a "trait" is a new construct or just
a way to import fields/methods from a  class.  I like the idea of a formal
new type so we can limit the scope of the feature instead of taking on the
baggage of an old type like "object".  I'll wait to hear from the compiler
team however.


I don't see what baggage there is ?

You said it yourself: internally it will just be an object. Just make it 
formally so.

I also think the argument of reusing existing objects deserves consideration.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 8:41 AM, Michael Van Canneyt  
> wrote:
> 
> I heavily object to this; We have way too much keywords as it is already. So 
> unless there really is no other way I don't think we should introduce
> even more.

I guess the first question is whether a "trait" is a new construct or just a 
way to import fields/methods from a  class. I like the idea of a formal new 
type so we can limit the scope of the feature instead of taking on the baggage 
of an old type like "object". I'll wait to hear from the compiler team however.

Looking back at the old thread Sven said:

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

so the idea wasn't rejected outright but I'm not sure if this idea even had 
their consent. I think traits/mixins are the way of the future for OOP but 
that's not certain for FPC yet. For example C# is a major language which is 
still single-inheritance only.

btw, here are some links I've gathered on how other languages support this 
concept:

• PHP (trait): https://www.php.net/manual/en/language.oop5.traits.php
• Python (mixin): https://devtut.github.io/python/mixins.html#mixin
• Swift (default protocol):
• https://nshipster.com/swift-default-protocol-implementations/
• http://machinethink.net/blog/mixins-and-traits-in-swift-2.0/

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Michael Van Canneyt via fpc-pascal



On Wed, 10 Feb 2021, Ryan Joseph via fpc-pascal wrote:





On Feb 10, 2021, at 12:32 AM, Michael Van Canneyt via fpc-pascal 
 wrote:

Instead of trait you might as well use simply "object", so basically we
could simply state that instead of an interface, you can also specify an object
in the class definition:


TMyClass = class (TParent,TMyTrait1,TMyTrait2)
Public
 property Trait1: TMyTrait1 read FTrait1 implements TMyTrait1;
 property Trait2: TMyTrait2 read FTrait2 implements TMyTrait2;
end;



Here's the old thread 
http://free-pascal-general.1045716.n5.nabble.com/Interface-delegates-and-the-implements-property-specifier-td5734729.html#a5734741.
 I think we agreed on introducing "trait" because it makes it clear that they 
are not objects and have other limitations. Under the hood they will be objects but it's 
important that the new type exists so the compiler can limit certain feature, like 
operator overloads and constructors (see below).


I heavily object to this; We have way too much keywords as it is already. 
So unless there really is no other way I don't think we should introduce

even more.

The compiler can do the check on an object for allowed/disallowed features
as soon as it is used in a 'trait'. So there really is no advantage gained 
by using 'trait'. The check just happens in a different place: not in the
object definition, but when it is used as a trait. At that point you must 
check for duplicate names (and probably other things) anyway.


Using 'object' has the additional advantage that you can simply use existing
objects, if they fulfill any restrictions, as traits.

If you impose the use of 'trait'  you risk that you must define objects twice 
to be able to use them as a trait - once standalone, once as a trait.


In Javascript you also don't need to use special classes/objects for mixins.

I really do not see a good reason to introduce a new type and matching
keyword.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Ryan Joseph via fpc-pascal


> On Feb 10, 2021, at 12:32 AM, Michael Van Canneyt via fpc-pascal 
>  wrote:
> 
> Instead of trait you might as well use simply "object", so basically we
> could simply state that instead of an interface, you can also specify an 
> object
> in the class definition:
> 
> 
> TMyClass = class (TParent,TMyTrait1,TMyTrait2)
> Public
>  property Trait1: TMyTrait1 read FTrait1 implements TMyTrait1;
>  property Trait2: TMyTrait2 read FTrait2 implements TMyTrait2;
> end;
> 

Here's the old thread 
http://free-pascal-general.1045716.n5.nabble.com/Interface-delegates-and-the-implements-property-specifier-td5734729.html#a5734741.
 I think we agreed on introducing "trait" because it makes it clear that they 
are not objects and have other limitations. Under the hood they will be objects 
but it's important that the new type exists so the compiler can limit certain 
feature, like operator overloads and constructors (see below).

What does putting them in the class hierarchy gain us? implementing duplicate 
traits is always going to be an error one way or another. I guess it's nice to 
see in the class name at least. I'll wait to hear what the compiler people 
think about this.

Again I think it was requested to implement traits using properties but in the 
simplest form all the compiler really needs to know is that 1) there is a field 
in the class and 2) the field if of type "trait". Even this would be good 
enough in theory:

type
  TMyClass = class
FTrait: TSomeTrait;
  end;

> I also don't think you should forbid constructors; In fact I think they are
> necessary if you want to support polymorphism, because I think they 
> create/set the VMT ?
> (the compiler people need to confirm this)

I think the VMT is created by adding virtual to a method in the class 
hierarchy. This is the only potentially complicated part of the feature and one 
I have lots of questions about (see my notes on the git page).

Constructors don't make sense because traits can never be instantiated by the 
programmer. There could be lifetime hooks, like AfterImplemented(parent: 
TObject) that the compiler calls automatically when a class is instantiated 
(like AfterConstruction that all Object classes can override). In fact traits 
should probably have a root class which is hidden, just like TObject.

> 
> The only obvious thing missing in your proposal is how this changes the RTTI 
> of the
> objects.

Good question. My plan currently is to make trait fields be implemented via 
automatically generated properties simply because you get lots of free 
functionally.

For methods I'm going to leverage what I'm learning from implementing "implicit 
function specialization", which is finally getting reviewed and moving forward 
after more than a year of limbo. The basic idea of overloading is the same so 
it will be easy enough for me to hook this up. The methods don't really exist 
however so they won't exist in the RTTI. Overriding methods will have bodies 
and probably work with the RTTI.

Regards,
Ryan Joseph

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-10 Thread Adriaan van Os via fpc-pascal

Michael Van Canneyt via fpc-pascal wrote:



On Tue, 9 Feb 2021, Ryan Joseph via fpc-pascal wrote:

We had talked about this some time ago and it's been rattling around 
in my brain so I wanted to write it down into a formal proposal where 
we can discuss it and hopefully agree upon a syntax. Everything is 
preliminary and tentative but this is a syntax which allows a 
"composition over inheritance" model, also known as "mix-ins" in some 
languages. That idea is similar to multiple inheritance except you 
have a concrete reference to the trait being implemented so you can 
resolve conflicts easily.


Here's what I have so far. Please feel free to look at it and give any 
feedback.


https://github.com/genericptr/freepascal/wiki/Traits-Proposal


In general, I am charmed by this idea.


I am, in whatever form it is implemented, also a proponent of the basic idea behind it, formulated 
on Ryan's  page as:


"Traits allow a "composition over inheritance" model which complement the existing single 
inheritance model introduced in Object Pascal. Traits are similar to multiple inheritance in 
languages such as C++ and Java, with the difference being an explicit reference to the class (or 
trait) which is being implemented."


Regards,

Adriaan van Os

___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal


Re: [fpc-pascal] Traits Proposal

2021-02-09 Thread Michael Van Canneyt via fpc-pascal



On Tue, 9 Feb 2021, Ryan Joseph via fpc-pascal wrote:


We had talked about this some time ago and it's been rattling around in my brain so I wanted to 
write it down into a formal proposal where we can discuss it and hopefully agree upon a syntax. 
Everything is preliminary and tentative but this is a syntax which allows a "composition over 
inheritance" model, also known as "mix-ins" in some languages. That idea is similar 
to multiple inheritance except you have a concrete reference to the trait being implemented so you 
can resolve conflicts easily.

Here's what I have so far. Please feel free to look at it and give any feedback.

https://github.com/genericptr/freepascal/wiki/Traits-Proposal


In general, I am charmed by this idea.

But I don't see the need to extend/change existing syntax, and in fact I highly
object to it.

Instead of trait you might as well use simply "object", so basically we
could simply state that instead of an interface, you can also specify an object
in the class definition:


TMyClass = class (TParent,TMyTrait1,TMyTrait2)
Public
  property Trait1: TMyTrait1 read FTrait1 implements TMyTrait1;
  property Trait2: TMyTrait2 read FTrait2 implements TMyTrait2;
end;

So no syntax changes are needed. Any restrictions on the objects in the
"traits" can be handled by the compiler.

The only difference with Interfaces is that you MUST have a property with
'Implements' if you specify an object. If you do not, the compiler complains.

It will also automatically mean that you can only have 1 trait of a certain
type, since the 
TMyClass = class (TParent,TMyTrait1,TMyTrait2)

cannot contain duplicates.

I also don't think you should forbid constructors; In fact I think they are
necessary if you want to support polymorphism, because I think they create/set 
the VMT ?
(the compiler people need to confirm this)

The only obvious thing missing in your proposal is how this changes the RTTI of 
the
objects.

Michael.
___
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal