Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-09

with in classes/records

44 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

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

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 10 lines
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.

Reply by email

Ryan Joseph
to Michael Van Canneyt
On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt 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

Show quoted text 24 lines
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.

Show quoted text 8 lines
> 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

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 11 lines
> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt 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.

Show quoted text 11 lines
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.

Show quoted text 7 lines
> 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 '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.

Show quoted text 5 lines
> > 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.

Show quoted text 43 lines
> > 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.

Show quoted text 9 lines
>> 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.

Reply by email

Maciej Izak
to Ryan Joseph: I’m sorry to bring this one up again but it was touched upon in regards to management…

pon., 3 wrz 2018 o 09:15 Ryan Joseph
napisał(a):

Show quoted text 15 lines
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

Reply by email

Ryan Joseph
to Michael Van Canneyt: I strongly feel that preventing thinking is an insult for any intelligent person. We're…
Show quoted text 14 lines
On Sep 3, 2018, at 4:14 PM, Michael Van Canneyt wrote: >> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt 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.

Show quoted text 18 lines
> > 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.

Show quoted text 5 lines
?? Default is not '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.

Show quoted text 9 lines
> 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.

Show quoted text 16 lines
> > 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.

Show quoted text 10 lines
>>> 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

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 19 lines
> On Sep 3, 2018, at 4:14 PM, Michael Van Canneyt wrote: > >>> On Sep 3, 2018, at 2:41 PM, Michael Van Canneyt 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.

Show quoted text 5 lines
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.
Show quoted text 20 lines
>> 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.

Reply by email

Ryan Joseph
to Michael Van Canneyt
Show quoted text 28 lines
On Sep 3, 2018, at 8:16 PM, Michael Van Canneyt 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.

Show quoted text 8 lines
> > 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.

Show quoted text 6 lines
>> 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

Reply by email

Martin
to Ryan Joseph
Ryan Joseph wrote 11 lines
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.)

Reply by email

Ryan Joseph
to Martin
Show quoted text 13 lines
On Sep 3, 2018, at 9:17 PM, Martin 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

Reply by email

Ryan Joseph
to Michael Van Canneyt: See below. It is not redundant. 1. The compiler needs to know what field it maps to. 2.…
On Sep 3, 2018, at 8:16 PM, Michael Van Canneyt 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 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

Reply by email

Ryan Joseph
to Martin: No it is not the same. You.f.Free; will always work, it is not ambiguous. You.Free;…
Show quoted text 7 lines
On Sep 3, 2018, at 9:17 PM, Martin 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

Reply by email

Martin
to Ryan Joseph
Ryan Joseph wrote 11 lines
> On Sep 3, 2018, at 9:17 PM, Martin 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.

Reply by email

Michael Van Canneyt
to Ryan Joseph: I mean given that exact configuration calling Free arrives at the same location so it’s…
Ryan Joseph wrote 17 lines
> On Sep 3, 2018, at 9:17 PM, Martin 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.

Reply by email

Sven Barth
to Ryan Joseph: I’m sorry to bring this one up again but it was touched upon in regards to management…
Ryan Joseph wrote 7 lines
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

Reply by email

Ryan Joseph
to Sven Barth
Show quoted text 14 lines
On Sep 4, 2018, at 12:35 PM, Sven Barth via fpc-pascal 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

Reply by email

Ryan Joseph
to Sven Barth: (Note: I read the other messages in the thread, but I'm replying here for brevity's sake)…

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

Reply by email

Ryan Joseph
to Ryan Joseph
On Sep 4, 2018, at 1:57 PM, Ryan Joseph 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

Reply by email

Ryan Joseph
to Maciej Izak: pon., 3 wrz 2018 o 09:15 Ryan Joseph napisał(a): you can always track my work on…
On Sep 3, 2018, at 4:39 PM, Maciej Izak 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

Reply by email

Ryan Joseph
to Ryan Joseph: Sorry I didn’t think enough before I sent this. We *must* allow this assignment to make…
Show quoted text 10 lines
On Sep 4, 2018, at 2:06 PM, Ryan Joseph 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

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Di., 4. Sep. 2018,
11:27:

Show quoted text 24 lines
> On Sep 4, 2018, at 2:06 PM, Ryan Joseph 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.

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.

Show quoted text 10 lines
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

Reply by email

Ryan Joseph
to Sven Barth
On Sep 4, 2018, at 7:15 PM, Sven Barth via fpc-pascal wrote: 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

Reply by email

Ryan Joseph
to Sven Barth: Ryan Joseph schrieb am Di., 4. Sep. 2018, 11:27: Would you please stop thinking with the…
On Sep 4, 2018, at 7:15 PM, Sven Barth via fpc-pascal wrote: 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

Reply by email

Ryan Joseph
to Ryan Joseph
On Sep 5, 2018, at 11:22 AM, Ryan Joseph wrote: 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

Reply by email

Ryan Joseph
to Sven Barth: (Note: I read the other messages in the thread, but I'm replying here for brevity's sake)…
Show quoted text 5 lines
On Sep 4, 2018, at 12:35 PM, Sven Barth via fpc-pascal 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.

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

Reply by email

Michael Van Canneyt
to Ryan Joseph: I just realized that default shouldn’t be allowed on non-object fields right? I think…
Ryan Joseph wrote 12 lines
> On Sep 4, 2018, at 7:15 PM, Sven Barth via fpc-pascal wrote: > > 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.

Show quoted text 7 lines
Regards, Ryan Joseph _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Ryan Joseph: Are default properties always read/write or can they be any combination? As they’re…
Ryan Joseph wrote 9 lines
> On Sep 5, 2018, at 11:22 AM, Ryan Joseph wrote: > > 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.

Reply by email

Ryan Joseph
to Michael Van Canneyt: No, the whole point of default is that they should be for any kind of field. For example…
Show quoted text 15 lines
On Sep 6, 2018, at 9:25 PM, Michael Van Canneyt wrote: 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

Reply by email

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Do., 6. Sep. 2018,
16:33:

Show quoted text 24 lines
> On Sep 6, 2018, at 9:25 PM, Michael Van Canneyt wrote: > > 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.

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

Reply by email

Ryan Joseph
to Sven Barth

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

Reply by email

Martin
to Ryan Joseph
Ryan Joseph wrote
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)

Reply by email

Ryan Joseph
to Martin
On Sep 9, 2018, at 5:01 PM, Martin wrote: 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

Reply by email

Sven Barth
to Michael Van Canneyt
Michael Van Canneyt wrote 10 lines
On Sun, 9 Sep 2018, Ryan Joseph wrote: > 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

Reply by email

Michael Van Canneyt
to Sven Barth
Sven Barth wrote 12 lines
Am 09.09.2018 um 16:16 schrieb Michael Van Canneyt: > > > On Sun, 9 Sep 2018, Ryan Joseph wrote: > >> 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:

I know that, but I meant you cannot immediatly go to the second level. You must specify the 2 indexes as in:

Show quoted text 5 lines
  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.

Reply by email

Ryan Joseph
to Michael Van Canneyt
Show quoted text 5 lines
On Sep 10, 2018, at 6:34 AM, Michael Van Canneyt wrote: I know that, but I meant you cannot immediatly go to the second level. You must specify the 2 indexes as in: > 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

Reply by email

Ryan Joseph
to Ryan Joseph

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

Reply by email

Ryan Joseph
to Ryan Joseph

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

Reply by email

Benito van der Zander
to Ryan Joseph: How should this syntax work? TWrapper needs to allow assignments of TWrapper for…

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

Ryan Joseph wrote 29 lines
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 _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Ryan Joseph
to Benito van der Zander
Show quoted text 14 lines
On Sep 26, 2018, at 1:14 AM, Benito van der Zander wrote: 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

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 24 lines
> On Sep 26, 2018, at 1:14 AM, Benito van der Zander wrote: > > 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.

Reply by email

Ryan Joseph
to Michael Van Canneyt
On Oct 4, 2018, at 8:34 PM, Michael Van Canneyt wrote: 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

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 14 lines
> On Oct 4, 2018, at 8:34 PM, Michael Van Canneyt wrote: > > 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.

Reply by email

Ryan Joseph
to Michael Van Canneyt
On Oct 4, 2018, at 9:19 PM, Michael Van Canneyt wrote: 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

Reply by email