Discussion:
[fpc-pascal] with in classes/records
Ryan Joseph
2018-09-03 07:15:08 UTC
Permalink
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listi
Michael Van Canneyt
2018-09-03 07:41:52 UTC
Permalink
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.
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.
Ryan Joseph
2018-09-03 08:18:21 UTC
Permalink
Post by Michael Van Canneyt
'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;
Post by Michael Van Canneyt
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.
Post by Michael Van Canneyt
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
Post by Michael Van Canneyt
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.
Post by Michael Van Canneyt
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.o
Michael Van Canneyt
2018-09-03 09:14:25 UTC
Permalink
Post by Michael Van Canneyt
'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.
Post by Michael Van Canneyt
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.
Post by Michael Van Canneyt
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.
Post by Michael Van Canneyt
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.
C.B := 100
and expands it to
C.FA.B := 100
by adding the “FA” subscript node
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.
Post by Michael Van Canneyt
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.
Ryan Joseph
2018-09-03 10:12:14 UTC
Permalink
Post by Michael Van Canneyt
Post by Ryan Joseph
Post by Michael Van Canneyt
'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.
Post by Michael Van Canneyt
Post by Ryan Joseph
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.
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.
Post by Michael Van Canneyt
?? 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.
Post by Michael Van Canneyt
Post by Ryan Joseph
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.
Post by Michael Van Canneyt
Post by Ryan Joseph
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.
Post by Michael Van Canneyt
Post by Ryan Joseph
Post by Michael Van Canneyt
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/lis
Michael Van Canneyt
2018-09-03 13:16:05 UTC
Permalink
Post by Michael Van Canneyt
Post by Michael Van Canneyt
'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.
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.
Post by Michael Van Canneyt
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.
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.
Post by Michael Van Canneyt
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.
Ryan Joseph
2018-09-03 13:56:40 UTC
Permalink
Post by Ryan Joseph
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.
Post by Ryan Joseph
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.
Post by Ryan Joseph
Post by Michael Van Canneyt
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailm
Martin
2018-09-03 14:17:19 UTC
Permalink
Post by Ryan Joseph
You.Free;
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 - fpc-***@lists.freepascal.org
http://lists.f
Ryan Joseph
2018-09-03 15:00:50 UTC
Permalink
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pasc
Martin
2018-09-03 16:07:36 UTC
Permalink
Post by Ryan Joseph
Post by Ryan Joseph
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.
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).
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailma
Ryan Joseph
2018-09-03 14:47:00 UTC
Permalink
Post by Martin
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/l
Michael Van Canneyt
2018-09-03 16:35:46 UTC
Permalink
Post by Martin
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.
Ryan Joseph
2018-09-03 14:54:40 UTC
Permalink
Post by Michael Van Canneyt
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 - fpc-***@lists.freepascal.org
http://lists.freepasca
Sven Barth via fpc-pascal
2018-09-04 05:35:57 UTC
Permalink
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pas
Ryan Joseph
2018-09-04 06:48:56 UTC
Permalink
Post by Sven Barth via fpc-pascal
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pas
Ryan Joseph
2018-09-04 06:57:42 UTC
Permalink
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fp
Ryan Joseph
2018-09-04 07:06:10 UTC
Permalink
Post by Ryan Joseph
// 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 - fpc-***@lists.freepascal.org
http://lists.fre
Ryan Joseph
2018-09-04 09:27:02 UTC
Permalink
Post by Ryan Joseph
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pasc
Sven Barth via fpc-pascal
2018-09-04 12:15:00 UTC
Permalink
Post by Ryan Joseph
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.
Would you please stop thinking with the C operators? They are merely
syntactic sugar and they don't exist by themselves. For this topic at least
please stick to their full versions (in your example "rec := rec + 10") as
that highlights better what the compiler needs to handle. In this case both
the + *and* the assignment operator.
Post by Ryan Joseph
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.
The idea of the default property is that *all* operators (and methods)
(except management operators) are hoisted from the type of the default
property. The assignment of one record with default property to another of
the same type is handled by the Copy management operator.

Regards,
Sven
Ryan Joseph
2018-09-04 15:30:37 UTC
Permalink
Would you please stop thinking with the C operators? They are merely syntactic sugar and they don't exist by themselves. For this topic at least please stick to their full versions (in your example "rec := rec + 10") as that highlights better what the compiler needs to handle. In this case both the + *and* the assignment operator.
Sorry, please bear with me.

I think this is just a precedence issue I raised. If TWrapper has a + operator then:

wrapper := wrapper + 1

should call the + operator, because base class takes precedence over the field num: integer. Correct? If TWrapper doesn’t have a + operator then:

wrapper := wrapper + 1 should resolve to wrapper.num := wrapper.num + 1

right?

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

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bi
Ryan Joseph
2018-09-05 04:22:50 UTC
Permalink
The idea of the default property is that *all* operators (and methods) (except management operators) are hoisted from the type of the default property. The assignment of one record with default property to another of the same type is handled by the Copy management operator.
I just realized that default shouldn’t be allowed on non-object fields right? I think that’s what was decided and makes most sense to me too. Disregard my last email. Sorry if I make a bunch of noise while I’m working on this but I’m probably going to have lots of questions coming up.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listi
Ryan Joseph
2018-09-06 04:44:59 UTC
Permalink
Post by Ryan Joseph
I just realized that default shouldn’t be allowed on non-object fields right? I think that’s what was decided and makes most sense to me too. Disregard my last email. Sorry if I make a bunch of noise while I’m working on this but I’m probably going to have lots of questions coming up.
Are default properties always read/write or can they be any combination? As they’re intended to be used it seems like they’re always both but maybe the option should be left open.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/list
Michael Van Canneyt
2018-09-06 14:25:48 UTC
Permalink
Post by Ryan Joseph
I just realized that default shouldn’t be allowed on non-object fields right? I think that’s what was decided and makes most sense to me too. Disregard my last email. Sorry if I make a bunch of noise while I’m working on this but I’m probably going to have lots of questions coming up.
Are default properties always read/write or can they be any combination?
As they’re intended to be used it seems like they’re always both but maybe
the option should be left open.
The option should be open. We don't know what the possible use cases will
be.

Michael.
Michael Van Canneyt
2018-09-06 14:25:13 UTC
Permalink
The idea of the default property is that *all* operators (and methods) (except management operators) are hoisted from the type of the default property. The assignment of one record with default property to another of the same type is handled by the Copy management operator.
I just realized that default shouldn’t be allowed on non-object fields
right? I think that’s what was decided and makes most sense to me too.
Disregard my last email. Sorry if I make a bunch of noise while I’m
working on this but I’m probably going to have lots of questions coming
up.
No, the whole point of default is that they should be for any kind of field.
For example if you want a nullable boolean, you'll do something like

Type
TNullable<T>= Record
Private
F : T;
isAssigned : Boolean;
Function GetValue : T;
Procedure SetValue(aValue : T);
Property Value : T Read GetValue Write SetValue; default;
Property IsNull : Boolean Read GetISNull Write SetIsNull;
end;

Var
B : TNullable<Boolean>;

begin
Writeln(B.IsNull); // Should be true
B:=True; // Works due to default.
Writeln(B.IsNull); // Should be false.
end.

Michael.
Regards,
Ryan Joseph
_______________________________________________
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Ryan Joseph
2018-09-06 14:32:47 UTC
Permalink
Post by Michael Van Canneyt
No, the whole point of default is that they should be for any kind of field.
For example if you want a nullable boolean, you'll do something like
Type
TNullable<T>= Record
Private
F : T;
isAssigned : Boolean;
Function GetValue : T;
Procedure SetValue(aValue : T);
Property Value : T Read GetValue Write SetValue; default;
Property IsNull : Boolean Read GetISNull Write SetIsNull;
end;
Thanks for the example. I didn’t even know it was intended for the default property to have functions as the read/write values. I thought read/write was always going to map directly to a field.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listi
Sven Barth via fpc-pascal
2018-09-06 15:08:43 UTC
Permalink
Post by Ryan Joseph
Post by Michael Van Canneyt
No, the whole point of default is that they should be for any kind of
field.
Post by Michael Van Canneyt
For example if you want a nullable boolean, you'll do something like
Type
TNullable<T>= Record
Private
F : T;
isAssigned : Boolean;
Function GetValue : T;
Procedure SetValue(aValue : T);
Property Value : T Read GetValue Write SetValue; default;
Property IsNull : Boolean Read GetISNull Write SetIsNull;
end;
Thanks for the example. I didn’t even know it was intended for the default
property to have functions as the read/write values. I thought read/write
was always going to map directly to a field.
No, it was always (as far as the idea exists at least) the idea that all
abilities of a property should be available for non-indexed default
properties as well.

Regards,
Sven
Ryan Joseph
2018-09-09 08:48:11 UTC
Permalink
It seems syntacticly possible that default properties could be recursive by having a default property reference a record/class with another default property. Should that be allowed?

type
THelperB = record
field: integer;
end;

type
THelperA = record
obj: THelperB;
property helperB: THelperB read obj write obj; default;
end;


type
TWrapper = record
obj: THelperA;
property helperA: THelperA read obj write obj; default;
end;


var
wrapper: TWrapper;
begin
wrapper.field := 100; // wrapper.helperA.helperB.field := 100

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.free
Martin
2018-09-09 10:01:25 UTC
Permalink
Post by Ryan Joseph
It seems syntacticly possible that default properties could be recursive by having a default property reference a record/class with another default property. Should that be allowed?
And how should the rules be for mixing with class helpers? (In case of
conflicting names)
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bi
Ryan Joseph
2018-09-09 12:58:49 UTC
Permalink
And how should the rules be for mixing with class helpers? (In case of conflicting names)
On the dev list Sven told me to use the tcallcandidates class to resolve overloads and I believe this handles searching class helpers also. So I guess the answer is whatever is standard behavior in FPC.

Since the base class takes precedence the example would search in the order:

TWrapper
THelperA
THelperB

If that’s allowed it’s basically the same as subclassing but in records.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lis
Michael Van Canneyt
2018-09-09 14:16:04 UTC
Permalink
Post by Ryan Joseph
It seems syntacticly possible that default properties could be recursive by having a default property reference a record/class with another default property. Should that be allowed?
I don't think so. Let's start with 1 level.
For default arrays it's also only one level deep.

Michael.
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepasc
Sven Barth via fpc-pascal
2018-09-09 19:18:44 UTC
Permalink
Post by Michael Van Canneyt
Post by Ryan Joseph
It seems syntacticly possible that default properties could be
recursive by having a default property reference a record/class with
another default property. Should that be allowed?
I don't think so. Let's start with 1 level.
For default arrays it's also only one level deep.
Ehm... news flash:

=== code begin ===

{$mode objfpc}

type
  TTest1 = class
  private
    fField: LongInt;
    function GetStuff(Index: Integer): Integer;
  public
    property Stuff[Index: Integer]: Integer read GetStuff; default;
  end;

  TTest2 = class
  private
    function GetTest(Index: Integer): TTest1;
  public
    property Test[Index: Integer]: TTest1 read GetTest; default;
  end;

{ TTest2 }

function TTest2.GetTest(Index: Integer): TTest1;
begin
  Result := TTest1.Create;
  Result.fField := Index;
end;

{ TTest1 }

function TTest1.GetStuff(Index: Integer): Integer;
begin
  Result := Index * fField;
end;

var
  Test: TTest2;
begin
  Test := TTest2.Create;
  Writeln(Test[21][2]);
end.

=== code end ===

Compiles and prints

=== output begin ===

42

=== output end ===

Though one can argue that default array properties are used explicitely
using the "[…]"-syntax while a non-array default property is
automatically triggered with ".".

Regards,
Sven
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.free
Michael Van Canneyt
2018-09-09 23:34:18 UTC
Permalink
Post by Michael Van Canneyt
Post by Ryan Joseph
It seems syntacticly possible that default properties could be
recursive by having a default property reference a record/class with
another default property. Should that be allowed?
I don't think so. Let's start with 1 level.
For default arrays it's also only one level deep.
I know that, but I meant you cannot immediatly go to the second level.
  Writeln(Test[21][2]);
Though one can argue that default array properties are used explicitely
using the "[
]"-syntax while a non-array default property is
automatically triggered with ".".
Yes.

Michael.
Ryan Joseph
2018-09-10 08:15:33 UTC
Permalink
Post by Sven Barth via fpc-pascal
Writeln(Test[21][2]);
The array property is a little different because it has the [] syntax which is effectively the name. There may be some interesting things to do with “default” being recursive but I’ll just comment it out for now.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listin
Ryan Joseph
2018-09-14 09:01:00 UTC
Permalink
Is a + operator that returns something other than the record valid?

I tried doing that but I get an error.

type
TWrapper = record
class operator + (left: TWrapper; right: integer): integer;
end;

var
wrapper: TWrapper;
i: integer;
begin
i := wrapper + 1; // ERROR: "TWrapper" expected “LongInt”

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freep
Benito van der Zander
2018-09-25 18:14:17 UTC
Permalink
Hi,

perhaps everything would be clearer, if the default property was
accessed with ^  ?


var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper^ := THelperA.Create;
end.


Cheers,
Benito
How should this syntax work? TWrapper needs to allow assignments of TWrapper for constructors but it also should accept assignments of THelperA for the default write property.
Is that a problem or should TWrapper be able to assign both? It looks strange so I thought I would ask.
==========================================
type
THelperA = class
end;
type
TWrapper = class
objA: THelperA;
property helperA: THelperA read objA write objA; default;
end;
var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper := THelperA.Create;
end.
Regards,
Ryan Joseph
_______________________________________________
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Ryan Joseph
2018-10-04 12:25:32 UTC
Permalink
Hi,
perhaps everything would be clearer, if the default property was accessed with ^ ?
var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper^ := THelperA.Create;
end.
Sorry, back to working on this today.

That’s an idea. Probably the other way around though since initializing the class is the less common operation. The compiler team is busy I guess because I haven’t got any answers recently on questions. I suspect appropriating the ^ symbol will not be ok with them since it has another reserved meaning.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.o
Michael Van Canneyt
2018-10-04 13:34:04 UTC
Permalink
Post by Ryan Joseph
Hi,
perhaps everything would be clearer, if the default property was accessed with ^ ?
var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper^ := THelperA.Create;
end.
Sorry, back to working on this today.
That’s an idea. Probably the other way around though since initializing
the class is the less common operation. The compiler team is busy I guess
because I haven’t got any answers recently on questions. I suspect
appropriating the ^ symbol will not be ok with them since it has another
reserved meaning.
Technical issues aside, it kind of defeats the purpose of the default
property...

Michael.
Ryan Joseph
2018-10-04 13:37:23 UTC
Permalink
Post by Michael Van Canneyt
Technical issues aside, it kind of defeats the purpose of the default
property...
Then just tolerate the fact we have a dual meaning for assignments? Looks wrong but maybe not a problem.

var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper := THelperA.Create;


Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.fre
Michael Van Canneyt
2018-10-04 14:19:25 UTC
Permalink
Post by Ryan Joseph
Post by Michael Van Canneyt
Technical issues aside, it kind of defeats the purpose of the default
property...
Then just tolerate the fact we have a dual meaning for assignments? Looks wrong but maybe not a problem.
var
wrapper: TWrapper;
begin
wrapper := TWrapper.Create;
wrapper := THelperA.Create;
And was this not the whole idea of introducing a default property in the first place ?

Michael.
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo
Ryan Joseph
2018-10-05 05:03:05 UTC
Permalink
Post by Michael Van Canneyt
And was this not the whole idea of introducing a default property in the first place ?
It is but I just wanted to make sure that this particular ambiguity with initializing classes wasn’t concerning for anyone.

So far I’ve identified 2 instances where the default needs to be ignored:

1) Assigning the same type i.e. during copies for records or init’ing classes
2) When passing into functions (like in writeln below we need to specify the default property so we don’t try to write the actual class)

Just making sure this is ok.

type
TWrapper = class
m_value: integer;
property value: integer read m_value write m_value; default;
end;

var
wrapper: TWrapper;
i: integer;
begin
wrapper := TWrapper.Create;
wrapper := 100;
wrapper := wrapper + 1;
writeln(wrapper.value);
i := wrapper; // error, this passes the class TWrapper
end.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lis

Ryan Joseph
2018-09-06 09:31:51 UTC
Permalink
Post by Sven Barth via fpc-pascal
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.
Here’s my usage example which I encountered and got stuck with.

The problem is I have a base class that has some commonly used functionality across many subclasses but at a later time I wanted to remove the fields from the class to save memory. Now I’m stuck with an OOP problem where I have tons of syntax like obj.RegisterMethod which are used in dozens of subclasses.

The solutions currently are:

1) Put the two manager concepts into separate classes which can be subclassed. This may work in some instances but in others it breaks class hierarchies. If I want to include both I’m in trouble also because there’s no multiple inheritance in Pascal.

2) Put the two manager concepts into separate delegate classes which are included manually (delphi has the “implements” property but this doesn’t really do anything to help). This is more flexible but now I need to replace all calls as obj.methodManager.RegisterMethod which is 1) messy/long 2) introduces more names to remember and 3) will require lots of copy/paste. If I ever want to change it back I have hours of copy/pasting to do.

type
TBaseObject = class
private
methods: array of TMethod;
objects: array of TObject;
public
{ method manager }
procedure RegisterMethod(name: string; proc: pointer);
procedure InvokeMethod(name: string);
{ object manager }
procedure ManageObject(obj: TObject);
end;

// if we need either of the 2 managers we need to inherit from TBaseObject
// and take on the baggage. Typical OOP problems with deep class hierarchies.

type
TMyObject = class (TBaseObject)
end;
var

obj: TMyObject;
begin
obj := TMyObject.Create;
obj.RegisterMethod('foo', @method);
obj.InvokeMethod('foo');
obj.ManageObject(otherObj);
end.

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

Using what I’ll call the “alias property” for now (using “with” is not proper grammar Sven mentioned, and “default” implies just one, so...) we can pull out the two manager concepts into separate records and include them manually into any class we want. Thanks to having management operators now we can get automatic init/finalize to manage the lifecycle but I didn’t show that here.

The alias property acts like the default property we’ve been discussing by calling into the fields namespace without the property name. This is the preferred solution for breaking up the class because once I add the manager record and the alias I don’t need to change any other code, everything works like before.

What I propose is that the default property be allowed to support multiple default properties. The default property already basically does this so it’s just a matter of performing some searches before determining which property to use for the given call. If we made this safe it would be a good alternative to subclassing and allow for some really nice delegation patterns we can’t do in Pascal now.

Clearly there are problems with name conflicts and precedence but surely they can be solved. There’s already a precedent of multiple interfaces in classes and redefining methods in subclasses so I don’t see why this can’t be made safe also. Please note the default property already introduces these problems so in my opinion it’s not that crazy to add more levels of search than just one.

Here’s what the class looks like broken up into delegates and using aliases so they calling conventions don’t change from the original.

type
TMethodManager = record
private
methods: array of TMethod;
public
procedure RegisterMethod(name: string; proc: pointer);
procedure InvokeMethod(name: string);
end;

type
TObjectManager = record
private
objects: array of TObject;
public
procedure ManageObject(obj: TObject);
end;

type
TMyObjectA = class
private
m_methodManager: TMethodManager;
property methodManager: TMethodManager alias m_methodManager;
end;

type
TMyObjectB = class
private
m_objectManager: TObjectManager;
property objectManager: TObjectManager alias m_objectManager;
end;

var
objA: TMyObjectA;
objB: TMyObjectB;
begin

objA := TMyObjectA.Create;
objA.RegisterMethod('foo', @method);
objA.InvokeMethod('foo’);

objB := TMyObjectB.Create;
objB.ManageObject(otherObj);
end.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/m
Maciej Izak
2018-09-03 09:39:15 UTC
Permalink
Post by Ryan Joseph
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
Ryan Joseph
2018-09-04 07:13:20 UTC
Permalink
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 - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/
Loading...