with in classes/records

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

with in classes/records

Ryan Joseph
I’m sorry to bring this one up again but it was touched upon in regards to management operators and auto free objects. Please bear with me while I recap.

To summarize what I said last time was that I wanted a way to include “with” statement functionality in classes and records to aid in delegation patterns (in leu of multiple inheritance in Pascal). As it turns out the idea is relevant to management operators and something similar using properties was requested by the developer of management operators (Maciej).

He suggested a “default” property which would basically delegate methods/field calls to another class so to avoid having to do things like "obj._.Free” which would be shorted to “obj.Free” because the field “_” is default. As he pointed out if such a default property existed I could leverage management operators to auto manage objects instead of adding anything more to the compiler like I did in my version. That’s a really important advancement for FPC and I’m personally very motivated in seeing this manifest in the language.

Maciej’s example:

TAutoCreate<T: class> = record
    _: T;
    property obj: T read _; default;
    class operator Initialize(var a: TAutoCreate<T>);
    class operator Finalize(var a: TAutoCreate<T>);
    class operator Copy(constref a: TAutoCreate<T>; var b: TAutoCreate<T>);
    class operator AddRef(var a: TAutoCreate<T>);
  end;

My suggestion I made some months ago is functionally identical except I wanted to add a “with” modifier to fields with no explicitly defined limit (or make it a section like public, or what ever, I don’t care really). i.e:

type
  TSomeManager = record
    procedure ManageThis;
  end;  

type
  TOtherManager = record
    procedure ManageThat;
  end;

type
  TMyRec = record
    delegateA: TSomeManager; with;
    delegateB: TOtherManager; with;
  end;

var
  c: TMyRec;
c.ManageThis; // delegateA is “with” so we can access it’s fields from TMyRec
c.ManageThat; // delegateB is “with” so we can access it’s fields from TMyRec

I’m writing this email today because I just had this exact same problem in my code and I’m desperate to make this work better in Pascal. :) Making auto free work as intended would be amazing also but we need to pass this hurdle first.

Thank you for bearing with me, so finally here are my questions:

1) Given this is critical to make management operators work smoothly what does the compiler team think about this idea to have a default property or “with" in classes/records?

2) If there is any plausible way the compiler team will allow this I’m willing to develop it myself to what every specification the team decides. I already looked into how this could be implemented and did some tests. It’s not complicated and within the range of something I could accomplish.

Please let me know what you guy think of this proposal.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Michael Van Canneyt


On Mon, 3 Sep 2018, Ryan Joseph wrote:

> I’m sorry to bring this one up again but it was touched upon in regards to management operators and auto free objects. Please bear with me while I recap.
>
> I’m writing this email today because I just had this exact same problem in
> my code and I’m desperate to make this work better in Pascal.  :) Making
> auto free work as intended would be amazing also but we need to pass this
> hurdle first.
>
> Thank you for bearing with me, so finally here are my questions:
>
> 1) Given this is critical to make management operators work smoothly what does the compiler team think about this idea to have a default property or “with" in classes/records?
'with' is terribly awkward.

If it needs to be done, please use 'default', it is in line with the default array property.

I suggest you only allow it on properties, not on fields.
In the case of records, this will automatically imply the use of advanced records,
and would prohibit it on classical records.

Make sure you establish precedence rules correctly. The default should only be
searched after all other properties/fields were handled.

TA = Class
   B : Integer;
end;

TB = record
Private
   FA : TA;
Public
   Property A : TA Read FA Write FA; default;
   B : Integer;
end;

Var
   C : TB;

begin
   C.B // Must refer to TB.B, not C.A.B
end.

I'm sure there are other pitfalls.

> 2) If there is any plausible way the compiler team will allow this I’m
> willing to develop it myself to what every specification the team decides.
> I already looked into how this could be implemented and did some tests.
> It’s not complicated and within the range of something I could accomplish.

Patches are always plausible.

Just dive in and ask questions. Help will surely be provided.
When you feel you're done, provide a patch. It will be considered like all
other patches.

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

Re: with in classes/records

Ryan Joseph


> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt <[hidden email]> wrote:
>
> 'with' is terribly awkward.

“with” came to mind because it’s basically a with statement but within a class. Properties had another side effect of requiring naming which is redundant (I’d probably just underscore the name always by default to prevent thinking).

I just learned about the management operator issue recently but my initial interest was for any number of properties to be added and thus “default” didn’t make lots of sense in my mind.

Properties also didn’t make sense to me because they’re always “read", i.e. “write" doesn’t make sense given the context (write would always be an ambiguous statement).

Doesn’t that look kind of redundant? The plus side is at least they get to hook into the property symbol class.

type
 TMyRec = record
   delegateA: TSomeManager;
   delegateB: TOtherManager;

   property _delegateA: T read delegateA; default;
   property _delegateB: T read delegateB; default;
 end;

>
> If it needs to be done, please use 'default', it is in line with the default array property.

In my first tests I found that indeed “default" was used for array properties and the modifier “default” was kind of tangled up for arrays. What is the thinking behind “default” for array properties anyways? I always add it because the compiler requires it but there’s probably more to it than that.

So yeah, the default keyword appeared to be reserved for arrays in the code so I wanted to make sure that’s really the correct approach.

>
> I suggest you only allow it on properties, not on fields. In the case of records, this will automatically imply the use of advanced records, and would prohibit it on classical records.

Not sure what you mean exactly. Not sure how it would look on field.s

>
> Make sure you establish precedence rules correctly. The default should only be
> searched after all other properties/fields were handled.
>
> TA = Class
>  B : Integer;
> end;
>
> TB = record
> Private
>  FA : TA;
> Public
>  Property A : TA Read FA Write FA; default;
>  B : Integer;
> end;
>
> Var
>  C : TB;
>
> begin
>  C.B // Must refer to TB.B, not C.A.B
> end.
>
> I'm sure there are other pitfalls.

You’re right, no doubt about pitfalls depending on how it works.

In my first naive test I just did some extra searches in searchsym_in_record and added a csubscriptnode node if a match was found. That works for records but I didn’t try to make generics work. Because this only works with classes/records I think it only makes sense for the property to be “read” right? All it does it basically takes:

C.B := 100

and expands it to

C.FA.B := 100

by adding the “FA” subscript node

if the property was write it would imply:

C := someA;

The context of the property “B” is “C” so “write" means assigning to “C” right? That obviously doesn’t make sense.

>
>> 2) If there is any plausible way the compiler team will allow this I’m
>> willing to develop it myself to what every specification the team decides. I already looked into how this could be implemented and did some tests. It’s not complicated and within the range of something I could accomplish.
>
> Patches are always plausible.
>
> Just dive in and ask questions. Help will surely be provided. When you feel you're done, provide a patch. It will be considered like all
> other patches.

Great news! I think there’s still some controversy ahead though about how to implement this.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Michael Van Canneyt


On Mon, 3 Sep 2018, Ryan Joseph wrote:

>
>
>> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt <[hidden email]> wrote:
>>
>> 'with' is terribly awkward.
>
> “with” came to mind because it’s basically a with statement but within a
> class.  Properties had another side effect of requiring naming which is
> redundant (I’d probably just underscore the name always by default to
> prevent thinking).
>
I strongly feel that preventing thinking is an insult for any intelligent person.

We're not "input monkeys" after all.


> I just learned about the management operator issue recently but my initial interest was for any number of properties to be added and thus “default” didn’t make lots of sense in my mind.
>
> Properties also didn’t make sense to me because they’re always “read", i.e. “write" doesn’t make sense given the context (write would always be an ambiguous statement).

Properties can be read/write/readwrite as the use case dictates.

>
> Doesn’t that look kind of redundant? The plus side is at least they get to hook into the property symbol class.
>
> type
> TMyRec = record
>   delegateA: TSomeManager;
>   delegateB: TOtherManager;
>
>   property _delegateA: T read delegateA; default;
>   property _delegateB: T read delegateB; default;
> end;
Obviously, only 1 default property should be allowed. Be it an array array or not.

Allowing multiple defaults will wreak havoc on precedence rules:
The order of property declarations would suddenly matter and that would of course be terribly wrong.
So that will certainly not be accepted.

The compiler could perfectly decide to reorder fields in a class if it thinks this is better
for alignment issues or whatnot.

But your example explains why you wanted to call it 'with', I fear that this would be
something very different from the "default" which Maciej had in mind,
although both result in similar possibilities.

My preference definitely goes to Maciej's idea, which allows for much less ambiguity.

Hence, only 1 default property.

>> If it needs to be done, please use 'default', it is in line with the default array property.
>
> In my first tests I found that indeed “default" was used for array
> properties and the modifier “default” was kind of tangled up for arrays.
> What is the thinking behind “default” for array properties anyways?  I
> always add it because the compiler requires it but there’s probably more
> to it than that.

?? Default is not required at all. It's only required if you want to write
A[i]
instead of
A.SomeProperty[i]

>
> So yeah, the default keyword appeared to be reserved for arrays in the code so I wanted to make sure that’s really the correct approach.

It really is.

>
>>
>> I suggest you only allow it on properties, not on fields. In the case of records, this will automatically imply the use of advanced records, and would prohibit it on classical records.
>
> Not sure what you mean exactly. Not sure how it would look on field.s

Same as on a property ?

A = Record
   f : Tfield; Default;
end;

I would not allow this.

>
>>
>> Make sure you establish precedence rules correctly. The default should only be
>> searched after all other properties/fields were handled.
>>
>> TA = Class
>>  B : Integer;
>> end;
>>
>> TB = record
>> Private
>>  FA : TA;
>> Public
>>  Property A : TA Read FA Write FA; default;
>>  B : Integer;
>> end;
>>
>> Var
>>  C : TB;
>>
>> begin
>>  C.B // Must refer to TB.B, not C.A.B
>> end.
>>
>> I'm sure there are other pitfalls.
>
> You’re right, no doubt about pitfalls depending on how it works.
>
> In my first naive test I just did some extra searches in searchsym_in_record and added a csubscriptnode node if a match was found. That works for records but I didn’t try to make generics work. Because this only works with classes/records I think it only makes sense for the property to be “read” right? All it does it basically takes:
>
> C.B := 100
>
> and expands it to
>
> C.FA.B := 100
>
> by adding the “FA” subscript node
>
> if the property was write it would imply:
>
> C := someA;
>
> The context of the property “B” is “C” so “write" means assigning to “C” right? That obviously doesn’t make sense.
It depends on type compatibility.

if C and someA are assignment compatible types, then the above will just assign
SomeA to C.

If C and someA are not assigment compatible types, then the compiler can
look for a default property (not an array) which is assignment compatible
with someA, and transform it to

C.FA:=SomeA;

You could decide to make this process recursive, but I would advise against
it, it will most likely have many unwanted side effects.

>>> 2) If there is any plausible way the compiler team will allow this I’m
>>> willing to develop it myself to what every specification the team decides. I already looked into how this could be implemented and did some tests. It’s not complicated and within the range of something I could accomplish.
>>
>> Patches are always plausible.
>>
>> Just dive in and ask questions. Help will surely be provided. When you feel you're done, provide a patch. It will be considered like all
>> other patches.
>
> Great news! I think there’s still some controversy ahead though about how to implement this.

No doubt - see above - but most of us are grown-up adults and can handle controversy in a civilized manner.

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

Re: with in classes/records

Maciej Izak
In reply to this post by Ryan Joseph
pon., 3 wrz 2018 o 09:15 Ryan Joseph <[hidden email]> napisał(a):
To summarize what I said last time was that I wanted a way to include “with” statement functionality in classes and records to aid in delegation patterns (in leu of multiple inheritance in Pascal). As it turns out the idea is relevant to management operators and something similar using properties was requested by the developer of management operators (Maciej).

He suggested a “default” property which would basically delegate methods/field calls to another class so to avoid having to do things like "obj._.Free” which would be shorted to “obj.Free” because the field “_” is default. As he pointed out if such a default property existed I could leverage management operators to auto manage objects instead of adding anything more to the compiler like I did in my version. That’s a really important advancement for FPC and I’m personally very motivated in seeing this manifest in the language.

you can always track my work on NewPascal and prepare/extract patch for FPC. The existing implementation of "default field" can be partially used for "default property without indexers" implementation (small note: the part of storing info for ppu for default field is really lame and should be done in other way - but works :P - this was one of my first work for compiler and should be reworked).

Please note that multiply default property is not best idea.

You can always wait for my implementation because I am working on this (in free time, feature rather expected at the end of year) and you can extract patch and submit to FPC (or create your own earlier if you wish). "Default field without indexer" will be needed for Delphi compatibility sooner or later (especially for nullable types).
--
Best regards,
Maciej Izak

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Michael Van Canneyt


> On Sep 3, 2018, at 4:14 PM, Michael Van Canneyt <[hidden email]> wrote:
>
>>> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt <[hidden email]> wrote:
>>> 'with' is terribly awkward.
>>
>> “with” came to mind because it’s basically a with statement but within a
>> class.  Properties had another side effect of requiring naming which is
>> redundant (I’d probably just underscore the name always by default to
>> prevent thinking).
>>
>
> I strongly feel that preventing thinking is an insult for any intelligent person.
>
> We're not "input monkeys" after all.

Fair enough. :) I was kind of joking but seriously the name is redundant and doesn’t provide any meaning because the name isn’t actually used. The only reason we have the name is because we’re trying to add this feature on top of properties syntax. Array properties had this oddity also and I would prefer to just omit the name since it’s meaningless.

Take the example of:

property _delegateA: TSomeManager read delegateA write delegateA; default;

What does naming it _delegateA add? the name is never used. Declaring the type and the read is also redundant. Just lots of typing.

Ideally all you really need in terms of information is this:

property delegateA; default;

I know it breaks property syntax but that’s all we really need.

Hope that makes sense. I just want to make for absolute certain using properties is the best approach.

>
>>
>> Doesn’t that look kind of redundant? The plus side is at least they get to hook into the property symbol class.
>>
>> type
>> TMyRec = record
>>  delegateA: TSomeManager;
>>  delegateB: TOtherManager;
>>
>>  property _delegateA: T read delegateA; default;
>>  property _delegateB: T read delegateB; default;
>> end;
>
> Obviously, only 1 default property should be allowed. Be it an array array or not.
>
> Allowing multiple defaults will wreak havoc on precedence rules:
> The order of property declarations would suddenly matter and that would of course be terribly wrong.
> So that will certainly not be accepted.

yeah, that’s the part I got pushback on before. Allow to make my case again. :)

Precedence is already skewed by allowing default in the first place so why is it so dangerous to allow further (optional) levels of introspection? I think we could find ways to make this safe if we thought about it some more.

Here’s my delegation example using classes:

type
 TSomeManager = class
   procedure ManageThis;
   procedure DoThis;
 end;  

type
 TOtherManager = class
   procedure ManageThat;
   procedure DoThis;
 end;

type
 TMyClass = class
   delegateA: TSomeManager; with; // I know, I just wrote it like this for brevity
   delegateB: TOtherManager; with; // COMPILER ERROR (see below): TOtherManager has duplicate method ‘DoThis' from TSomeManager
 end;

var
  c: TMyClass;

c.Free;  // what does Free do here??? TMyClass, delegateA and delegateB are all TObject's!

I think you said properties come after the current class so Free would be called on TMyClass. Is that really that dangerous? Personally I’m inclined to see the benefit than the potential dangers but I understand how this could go wrong. The with statement has existed forever and it certainly can cause bugs. That’s just how programming is I guess.

What about giving errors if naming conflicts arise? I don’t even think class helpers do that but those were allowed. In fact this is very similar to class helpers isn’t it?

We can make this is as safe as helpers I’m quite certain. I just had this problem today and needed to breakup a class hierarchy using delegation and it would be 100% safe. I’ll make an example later.

>
> ?? Default is not required at all. It's only required if you want to write
> A[i]
> instead of
> A.SomeProperty[i]

Ah, I should have guessed that. In my mind the sole reason to use array properties is so you so you can just do A[], never considered it otherwise.

>> Not sure what you mean exactly. Not sure how it would look on field.s
>
> Same as on a property ?
>
> A = Record
>  f : Tfield; Default;
> end;
>
> I would not allow this.

agreed. Doesn’t make sense for what we’re after.

>>
>> The context of the property “B” is “C” so “write" means assigning to “C” right? That obviously doesn’t make sense.
>
> It depends on type compatibility.
>
> if C and someA are assignment compatible types, then the above will just assign
> SomeA to C.
>
> If C and someA are not assigment compatible types, then the compiler can
> look for a default property (not an array) which is assignment compatible
> with someA, and transform it to
>
> C.FA:=SomeA;
>
> You could decide to make this process recursive, but I would advise against
> it, it will most likely have many unwanted side effects.

But why do we want to make this assignment? Now even I’m worried about ambiguous calls. ;) I guess because it’s a property we might as well do it because that is what properties allow but I feel like it’s being added just because we have to.

I’d like to hear if others think this is a useful operation to do. Honestly I’m still skeptical as to why this is being built on top of properties in the first place given all the redundancies.

>
>>>> 2) If there is any plausible way the compiler team will allow this I’m
>>>> willing to develop it myself to what every specification the team decides. I already looked into how this could be implemented and did some tests. It’s not complicated and within the range of something I could accomplish.
>>> Patches are always plausible.
>>> Just dive in and ask questions. Help will surely be provided. When you feel you're done, provide a patch. It will be considered like all
>>> other patches.
>>
>> Great news! I think there’s still some controversy ahead though about how to implement this.
>
> No doubt - see above - but most of us are grown-up adults and can handle controversy in a civilized manner.

That’s all in good fun, I’m just encouraged that something I need is being considered even though I have a questionable ambition to make it work like a with statement. That much aside it’s still good news for me.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Michael Van Canneyt


On Mon, 3 Sep 2018, Ryan Joseph wrote:

>
>
>> On Sep 3, 2018, at 4:14 PM, Michael Van Canneyt <[hidden email]> wrote:
>>
>>>> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt <[hidden email]> wrote:
>>>> 'with' is terribly awkward.
>>>
>>> “with” came to mind because it’s basically a with statement but within a
>>> class.  Properties had another side effect of requiring naming which is
>>> redundant (I’d probably just underscore the name always by default to
>>> prevent thinking).
>>>
>>
>> I strongly feel that preventing thinking is an insult for any intelligent person.
>>
>> We're not "input monkeys" after all.
>
> Fair enough.  :) I was kind of joking but seriously the name is redundant
> and doesn’t provide any meaning because the name isn’t actually used.
See below.

> The
> only reason we have the name is because we’re trying to add this feature
> on top of properties syntax.  Array properties had this oddity also and I
> would prefer to just omit the name since it’s meaningless.

It is not redundant.
1. The compiler needs to know what field it maps to.
2. You need it to disambiguate.

Me = Class
   A : me;
end;

You = class
   f : Me;
   property a : me Read f write F;
end;

You need to be able to do

Var
   b : me;

You.a:=b;
you.a.a:=b,

You need the name or you will not be able to disambiguate between the 2 cases.



> Take the example of:
>
> property _delegateA: TSomeManager read delegateA write delegateA; default;
>
> What does naming it _delegateA add? the name is never used. Declaring the type and the read is also redundant. Just lots of typing.


>>> Doesn’t that look kind of redundant? The plus side is at least they get to hook into the property symbol class.
>>>
>>> type
>>> TMyRec = record
>>>  delegateA: TSomeManager;
>>>  delegateB: TOtherManager;
>>>
>>>  property _delegateA: T read delegateA; default;
>>>  property _delegateB: T read delegateB; default;
>>> end;
>>
>> Obviously, only 1 default property should be allowed. Be it an array array or not.
>>
>> Allowing multiple defaults will wreak havoc on precedence rules:
>> The order of property declarations would suddenly matter and that would of course be terribly wrong.
>> So that will certainly not be accepted.
>
> yeah, that’s the part I got pushback on before. Allow to make my case again. :)
>
> Precedence is already skewed by allowing default in the first place so why is it so dangerous to allow further (optional) levels of introspection? I think we could find ways to make this safe if we thought about it some more.
Precedence is not skewed by 'default' if there is only 1 'default'.

1. Check Object
2. Check 1 member of object.
That's it.

The same was true for array. It only works because there is only 1 default allowed.

So this is not negotiable as far as I am concerned.

and, so it seems, for other people of the team.
I was unaware of that, but I agree with them.

>> it, it will most likely have many unwanted side effects.
>
> But why do we want to make this assignment? Now even I’m worried about ambiguous calls. ;) I guess because it’s a property we might as well do it because that is what properties allow but I feel like it’s being added just because we have to.

The whole point of 'default' is to be able to make this assignment, for example to implement nullable types.

As I said, your idea of 'with' is not exactly the same as 'default' on a simple property.
It shares some properties, but it is not the same.

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

Re: with in classes/records

Ryan Joseph


> On Sep 3, 2018, at 8:16 PM, Michael Van Canneyt <[hidden email]> wrote:
>
>> The
>> only reason we have the name is because we’re trying to add this feature
>> on top of properties syntax.  Array properties had this oddity also and I
>> would prefer to just omit the name since it’s meaningless.
>
> It is not redundant. 1. The compiler needs to know what field it maps to.
> 2. You need it to disambiguate.
>
> Me = Class
>  A : me;
> end;
>
> You = class
>  f : Me;
>  property a : me Read f write F;
> end;
>
> You need to be able to do
>
> Var
>  b : me;
>
> You.a:=b;
> you.a.a:=b,
>
> You need the name or you will not be able to disambiguate between the 2 cases.

Sure I understand in that example but that doesn’t apply with the default property. The very purpose that the property is default is so we don’t need to refer to the name. Correct?

The reason I’m so confused about the proposal to make it a property is because the name itself is disregarded and that’s kind of the entire reason for properties, ie., to make another name which acts an alias. Once you take the name out by making it default it’s not really a property anymore.

In your example if the “a” property was default than:

You.Free;

would be the same as:

You.f.Free;

right? I just don’t see where the name of the property applies. The property *removed* a name in fact. It’s like an anti-name. ;)

Please correct me where I’m wrong.

>>
>> Precedence is already skewed by allowing default in the first place so why is it so dangerous to allow further (optional) levels of introspection? I think we could find ways to make this safe if we thought about it some more.
>
> Precedence is not skewed by 'default' if there is only 1 'default'.
>
> 1. Check Object
> 2. Check 1 member of object.
> That's it.

But you can still have naming conflicts even with one level of indirection. Right? Class helpers and with statements both pose this same problem but we manage them for the benefits.

My logic is if we can do step #1 and step #2 then it follows that step #3 should be viable. It’s just an arbitrary restriction for some perceived level of safety and we lose functionality.

Did you not find my idea to give compiler errors compelling? I know we can make this safe and it’s a powerful feature for delegation patterns if the programmer can design the program properly.

>
> The same was true for array. It only works because there is only 1 default allowed.

Array is safe because there’s only one way [] can be interpreted.

>
> So this is not negotiable as far as I am concerned.
>
> and, so it seems, for other people of the team. I was unaware of that, but I agree with them.

My appeal is that we can make this safe and robust which will enable some really powerful delegation patterns that inheritance or class helpers can’t solve.

If I make a concrete example of a real world problem will that interest anyone? Maybe it’s not clear why this is useful and in leu of that I can understand why this sounds like a bad idea that will cause bugs.

>
>>> it, it will most likely have many unwanted side effects.
>>
>> But why do we want to make this assignment? Now even I’m worried about ambiguous calls. ;) I guess because it’s a property we might as well do it because that is what properties allow but I feel like it’s being added just because we have to.
>
> The whole point of 'default' is to be able to make this assignment, for example to implement nullable types.

Really? :) I thought it was for management operators so that they could pose as an alias for the wrapped type. The reason I reopened this “with” in classes is because it resembled the same aliasing idea that would allow delegation.

I guess this is a good time to ask, what are the other usage cases?

>
> As I said, your idea of 'with' is not exactly the same as 'default' on a simple property.
> It shares some properties, but it is not the same.

Indeed.



Regards,
        Ryan Joseph

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

Re: with in classes/records

Martin Friebe
On 03/09/2018 15:56, Ryan Joseph wrote:

>
> In your example if the “a” property was default than:
>
> You.Free;
>
> would be the same as:
>
> You.f.Free;
>
> right? I just don’t see where the name of the property applies. The property *removed* a name in fact. It’s like an anti-name. ;)
>

No it is not the same.

You.f.Free;
will always work, it is not ambiguous.

You.Free;
depends on no method Free being declared on the class of You, or any of its base classes, or any other default class (if more than one is allowed) that would be searched at higher priority.

You.Free;
has a risk, of suddenly and expectingly doing something else. Therefore it is not the same.
It does however take the same action, if and only if there is no other Free, but the one you wanted.

Example

TMyForm = class(TForm)
   property foo: TFoo; default;
end

TFoo has a method DoFoo. So you can do
MyForm.DoFoo

But unlike MyForm.Foo.DoFoo, the above will fail, if the LCL introduces TForm.DoFoo, which would then be used instead of your DoFoo)

Therefore the shorthand syntax can only be used if all classes are written by yourself. (And if you can trust yourself, to never add conflicting methods.)


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

Re: with in classes/records

Ryan Joseph


> On Sep 3, 2018, at 9:17 PM, Martin <[hidden email]> wrote:
>
> No it is not the same.
>
> You.f.Free;
> will always work, it is not ambiguous.
>
> You.Free;
> depends on no method Free being declared on the class of You, or any of its base classes, or any other default class (if more than one is allowed) that would be searched at higher priority.
>
> You.Free;
> has a risk, of suddenly and expectingly doing something else. Therefore it is not the same.
> It does however take the same action, if and only if there is no other Free, but the one you wanted.

I mean given that exact configuration calling Free arrives at the same location so it’s “the same”. The reason we made the default property was to *omit* the name of the property AND the name of the field it references.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Michael Van Canneyt


> On Sep 3, 2018, at 8:16 PM, Michael Van Canneyt <[hidden email]> wrote:
>
> The whole point of 'default' is to be able to make this assignment, for example to implement nullable types.

Just thought about this some more and realized that if you’re thinking more in terms of nullable types (which rely totally on assigning if I understand what you mean) than that’s why my idea to have multiple defaults just doesn't make any sense at all.

Does it make more sense if I say for “write” properties (like used for nullable types) there is only one default property allowed but for read-only there could be multiple properties? That mitigates some of the concern I think. If we add compiler errors I think we can make this is very safe feature.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Martin Friebe


> On Sep 3, 2018, at 9:17 PM, Martin <[hidden email]> wrote:
>
> You.f.Free;
> will always work, it is not ambiguous.
>
> You.Free;
> depends on no method Free being declared on the class of You, or any of its base classes, or any other default class (if more than one is allowed) that would be searched at higher priority.

sorry I just wanted to add, in the case of You.Free where there is an naming conflicting in super classes (Free in TObject of 3 classes) the rule is:

1) *always* use the base classes implementation regardless of order.
2) if you want an implementation other than the base class you must explicitly call on that field.

That should be easy to follow I think. Even if that’s not complete or correct we can assign very strict rules which force the programmer into explicit calls when naming conflicts exist.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Martin Friebe
On 03/09/2018 17:00, Ryan Joseph wrote:

>
>> On Sep 3, 2018, at 9:17 PM, Martin <[hidden email]> wrote:
>>
>> You.f.Free;
>> will always work, it is not ambiguous.
>>
>> You.Free;
>> depends on no method Free being declared on the class of You, or any of its base classes, or any other default class (if more than one is allowed) that would be searched at higher priority.
> sorry I just wanted to add, in the case of You.Free where there is an naming conflicting in super classes (Free in TObject of 3 classes) the rule is:
>
> 1) *always* use the base classes implementation regardless of order.
That is exactly by example. Base class takes priority over default property.

So you implement a "property f; default". At this time the base does not
have a Free method, so you write You.Free. That works.

Except at some later time, you or somebody else adds a Free method to
the base class. Then You.Free breaks.

Imagine the base class is in a package, used by many projects, then at
the time of adding to the base class, you can not check all the projects
(especially if the base package is published and used by others too).

> 2) if you want an implementation other than the base class you must explicitly call on that field.
So given the above, you can never safely use the shortcut, unless there
is no base class/inheritance.

But even then, if more than one default was allowed: The 2nd default
would not be safe, because the same method could be added to the first
default. Or to any default that the first default has, since this can be
nested indefinitely.

Btw, similar conflicts sometimes occur, if you add units to your project.
Given that there can be many levels of inheritance, or nested defaults,
while on the other hands units do not have nesting (symbols from units
used by a used unit, are not visible), the likelihood of conflicts with
default, is far more likely that with using units.

-------------
If all you look for is an easy way to simulate automatic memory
management, then it would be best to do so avoiding the many pitfalls of
multiple inheritance (though the above is only a subset of multi
inheritance)
Sorry I have no alternative proposal.

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

Re: with in classes/records

Michael Van Canneyt
In reply to this post by Ryan Joseph


On Mon, 3 Sep 2018, Ryan Joseph wrote:

>
>
>> On Sep 3, 2018, at 9:17 PM, Martin <[hidden email]> wrote:
>>
>> No it is not the same.
>>
>> You.f.Free;
>> will always work, it is not ambiguous.
>>
>> You.Free;
>> depends on no method Free being declared on the class of You, or any of its base classes, or any other default class (if more than one is allowed) that would be searched at higher priority.
>>
>> You.Free;
>> has a risk, of suddenly and expectingly doing something else. Therefore it is not the same.
>> It does however take the same action, if and only if there is no other Free, but the one you wanted.
>
> I mean given that exact configuration calling Free arrives at the same location so it’s “the same”. The reason we made the default property was to *omit* the name of the property AND the name of the field it references.
The point is that there are situations where you need this name.
Martin demonstrated this clearly.

So it must be specified in the declaration.

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

Re: with in classes/records

Free Pascal - General mailing list
In reply to this post by Ryan Joseph
On 03.09.2018 09:15, Ryan Joseph wrote:
> Thank you for bearing with me, so finally here are my questions:
>
> 1) Given this is critical to make management operators work smoothly what does the compiler team think about this idea to have a default property or “with" in classes/records?
>
> 2) If there is any plausible way the compiler team will allow this I’m willing to develop it myself to what every specification the team decides. I already looked into how this could be implemented and did some tests. It’s not complicated and within the range of something I could accomplish.
>
> Please let me know what you guy think of this proposal.

(Note: I read the other messages in the thread, but I'm replying here
for brevity's sake)

I think you need to be clearer what you want to achieve in the end. The
default property as intended by Maciej has the idea that it hoists the
operators of the default property type to the record it is contained in.
E.g. if the type has a "+" defined then using "+" on the record will use
it on the default field instead. From what I remember from one of your
previous mails your idea seems to be more to make other records part of
the record, basically like compositing. Here the question would be if
the operators of the contained type are at all hoisted to the container
type (especially if you have multiple ones and one of them defines a
certain operator and the other doesn't, not to forget other fields of
the container record).
So this would need to be explained/explored/specified first.

Also the way you used the "with" as a modifier is a no-go. If you look
at the other modifiers that Pascal uses ("public", "default",
"external", "alias", "deprecated", etc.) you'll notice that they're
either adjectives or nouns, but not a preposition. So if you want to
keep the "with" it needs to be used differently, not as a modifier (same
would be true for existing identifiers "uses" and "contains"), e.g.
"with SomeField: SomeType;". Or indeed a separate section like "public
contains" or so...

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

Re: with in classes/records

Ryan Joseph


> On Sep 4, 2018, at 12:35 PM, Sven Barth via fpc-pascal <[hidden email]> wrote:
>
> I think you need to be clearer what you want to achieve in the end. The
> default property as intended by Maciej has the idea that it hoists the
> operators of the default property type to the record it is contained in.
> E.g. if the type has a "+" defined then using "+" on the record will use
> it on the default field instead. From what I remember from one of your
> previous mails your idea seems to be more to make other records part of
> the record, basically like compositing. Here the question would be if
> the operators of the contained type are at all hoisted to the container
> type (especially if you have multiple ones and one of them defines a
> certain operator and the other doesn't, not to forget other fields of
> the container record).
> So this would need to be explained/explored/specified first.

Yeah I think what I should do is provide a clear usage case later but just develop it as a default property for now and see what comes from it. My suspicion is that the way it’s implemented it's going to provide a clear path for multiple paths of indirection which could be used for delegation and as an alternative to subclassing, but we’ll save that for later and with the provision it makes sense and can be made safe.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Free Pascal - General mailing list
I started in on this already and here’s the first conflict I found when trying operators.

What should happen if you assign a record to another record with a default property?

var
        wrapper: TWrapper;
        other: TWrapper;

// this should assign to ‘obj’ via the default property
wrapper := TObject.Create;

// what happens here? is this a wrong type error (TObject is expected but got TWrapper) or do we assign directly to the base record? I can see it both ways so I’m not sure what principle to fall back on. Allow it because we can or prevent it because it’s not intended functionality?
wrapper := other;

type
        TWrapper = record
                obj: TObject;
                property _default: TObject read obj write obj; default;
                class operator + (left: TWrapper; right: integer): TWrapper;
        end;

class operator TWrapper.+ (left: TWrapper; right: integer): TWrapper;
begin
        // error here
        result := left;
end;



Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph


> On Sep 4, 2018, at 1:57 PM, Ryan Joseph <[hidden email]> wrote:
>
> // what happens here? is this a wrong type error (TObject is expected but got TWrapper) or do we assign directly to the base record? I can see it both ways so I’m not sure what principle to fall back on. Allow it because we can or prevent it because it’s not intended functionality?
> wrapper := other;

Sorry I didn’t think enough before I sent this.

We *must* allow this assignment to make operator overloads work. +=  operators are also basically assigning TWrapper to TWrapper, right? I guess we need to break the default property behavior is instances that the same type is being assigned to itself but correct me if I’m wrong.

var
        wrapper: TWrapper;

wrapper += 10;

Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Maciej Izak


> On Sep 3, 2018, at 4:39 PM, Maciej Izak <[hidden email]> wrote:
>
> You can always wait for my implementation because I am working on this (in free time, feature rather expected at the end of year) and you can extract patch and submit to FPC (or create your own earlier if you wish). "Default field without indexer" will be needed for Delphi compatibility sooner or later (especially for nullable types).

Thank, I’m going to try an implementation by myself as a learning experience but I may be in over my head or make a really stupid implementation which isn’t suitable. ;) Either way I’ll let everyone know.

Regards,
        Ryan Joseph

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

Re: with in classes/records

Ryan Joseph
In reply to this post by Ryan Joseph


> On Sep 4, 2018, at 2:06 PM, Ryan Joseph <[hidden email]> wrote:
>
> Sorry I didn’t think enough before I sent this.
>
> We *must* allow this assignment to make operator overloads work. +=  operators are also basically assigning TWrapper to TWrapper, right? I guess we need to break the default property behavior is instances that the same type is being assigned to itself but correct me if I’m wrong.
>
> var
> wrapper: TWrapper;
>
> wrapper += 10;

Some questions about operator overloads.

1) rec := 1; should resolve to rec.num := 1 obviously.

2) rec += 10; should call the function TWrapper.+ right? It could operate directly on the field “num” but then the actual function wouldn’t be called.

3) should writeln(rec); resolve to writeln(rec.num); or be a syntax error? If it resolves to rec.num then passing around the record would technically just pass the num field and not the record. That doesn’t sound right to me. Without thinking about it much it feels like “rec” in isolation should be treated as the base type, ie. TWrapper.

4) I guess := operator overloads for records with a default property should be disabled, right? Otherwise they present a conflict that needs to be resolved.


Example code:


type
        TWrapper = record
                num: integer;
                property _default: integer read num write num; default;
                class operator + (left: TWrapper; right: integer): TWrapper;
        end;

class operator TWrapper.+ (left: TWrapper; right: integer): TWrapper;
begin
        left.num += right;
        result := left;
end;

var
        rec: TWrapper;
begin
        rec := 1; // rec.num := 1
        rec += 10; // as-is, TWrapper.+ is called
        writeln(rec); // syntax error, can’t write TWrapper?

Regards,
        Ryan Joseph

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