Recent

Author Topic: Why with allows assignment  (Read 40313 times)

440bx

  • Hero Member
  • *****
  • Posts: 4993
Re: Why with allows assignment
« Reply #75 on: January 06, 2025, 12:41:07 am »
Code: Pascal  [Select][+][-]
  1. if TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding = 0 then
  2.     TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding = 42;
verses :
Code: Pascal  [Select][+][-]
  1. with TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding as LeftPad do
  2. begin
  3.     if LeftPad = 0 then
  4.         LeftPad = 42;
  5.     X := Y;
  6.     ....
  7. end;

Saves a lot of error prone typing but more importantly, much, much easier to read. The use of "as LeftPad" leaves no doubt what object is being addressed, no one has to wonder the X or Y is also getting the "with treatment".
Davo
That's already available by defining a 'LeftPad' macro, no need to change the "with" statement to get that funcitonality.

(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

TRon

  • Hero Member
  • *****
  • Posts: 3936
Re: Why with allows assignment
« Reply #76 on: January 06, 2025, 12:51:31 am »
But the end result isn't the same.
There you are a bit in the wrong my friend or at least not precise enough. The value is assigned to in the scope but not outside that scope.

Outside the scope the value of V is still the value of V as it was before entering the scope.

Ergo the end-result is the same in that the value is not actually stored in(side) the result-value (as returned by the function).

Quote
And that's not the only problem creating a temporary as a result of a "with" statement creates.  if the "with" states multiple scopes, are there supposed to be temporaries for each one ?... it's so wrong, it's beyond belief it is being done.  Is the "with" statement going to create temporaries in some cases and not in others ? ... where are those cases documented ?
Yes, that is indeed problematic (or at least can be especially when in the case of the formentioned anonymous functions which by default introduces a new scope when created).


Quote
Another problem seems to be that there are people who are very willing to accept/support incorrect behavior because they find it convenient.  That's not a good thing but, admittedly that's a different problem though, it certainly doesn't help matters.
The problem is more that the compiler should flag it it as incorrect for both cases in OP's example (but at the same time OP's code is trying to hide the manipulation just as the absolute trick that once allowed for modifying an iterator) . For sure it is confusing which can been seen by the people running into these kind of things (remember/note that the compiler flagged the assignment as illegal only recently (in the grant scheme of its existence that is)).
« Last Edit: January 06, 2025, 12:57:36 am by TRon »
I do not have to remember anything anymore thanks to total-recall.

VisualLab

  • Hero Member
  • *****
  • Posts: 625
Re: Why with allows assignment
« Reply #77 on: January 06, 2025, 12:56:08 am »
In comparison to this topic, quantum dynamics appears to be more comprehensible.   :o

It's a matter of agreement between people as to what will be implemented and why. Meanwhile, there are rather two variants here:
  • classical, i.e. "with" is used to establish a scope,
  • heavily "overloaded", i.e. "with" is used for: A, B, C and D (and maybe even E the day after tomorrow).
This causes the language to become not only less readable but also less unambiguous (and the compiler code swells more and more). It's not even a matter of pragmatism. Because taking care of readability, clarity and unambiguity is very much pragmatism. It's for such reasons that Java or C# have become bloated over time (because you can't lag behind when various "modern" features of languages ​​appear).

I understand the supporters of "overloading" the "with" keyword (i.e. what do they mean). But in my opinion these benefits are ephemeral. Is it impossible to live without it? Because if something can be easily solved in another way, then these additional "overloads" of keywords1) are not even syntactic sugar.

This proposal reminds me of the C-style assignment operators that were introduced in FPC. And which were not needed at all, because we have stopped using slow and unreliable teletypes (instead of keyboards and monitors) for a long time now, to "reducing the number of key typings".



1) I.e. "with". And there were also suggestions to "overload" the "as".

440bx

  • Hero Member
  • *****
  • Posts: 4993
Re: Why with allows assignment
« Reply #78 on: January 06, 2025, 01:05:54 am »
There you are a bit in the wrong my friend or at least not precise enough. The value is assigned to in the scope but not outside that scope.
But, inside or outside the scope, a function result, outside the function, is _not_ an lvalue, therefore cannot be assigned to and, there should be no temporary and if there is a temporary representing the function result then it should behave exactly the same as the function result otherwise, the programmer should have copied the function result into a separate variable then, anything goes because it no longer represents the function's result.

Summary: a temporary should most definitely not exist as a result of the "with" but, if for the sake of argument, we accept the existence of a temporary to represent the function's result then it must behave exactly as the function's result which means, it cannot be changed because the temporary isn't in the scope of the function.

ETA: corrected a typo.
« Last Edit: January 06, 2025, 01:08:52 am by 440bx »
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

TRon

  • Hero Member
  • *****
  • Posts: 3936
Re: Why with allows assignment
« Reply #79 on: January 06, 2025, 01:18:56 am »
But, inside or outside the scope, a function result, outside the function, is _not_ an lvalue, therefore cannot be assigned to and, there should be no temporary and if there is a temporary representing the function result then it should behave exactly the same as the function result otherwise, the programmer should have copied the function result into a separate variable then, anything goes because it no longer represents the function's result.
Exactly right. We seem to agree on that (so was Warfley as far as I understood)

It is either this or that and not one time this and another time that. But as explained the OP's code obfuscate things probably to such an extend that the compiler is unable to pick up in that. Quite frankly I haven't bothered to see if it the compiler can be tricked in more obvious manners simply because the topic doesn't interest me as much.

Quote
Summary: a temporary should most definitely not exist as a result of the "with" but, if for the sake of argument, we accept the existence of a temporary to represent the function's result then it must behave exactly as the function's result which means, it cannot be changed because the temporary isn't in the scope of the function.
I am very tempted to agree to that but then, I am not a compiler developer or someone calling the shots (nor do I have any idea if the obfuscation of TS's code can be detected in an easy manner).

Note that it took over a decade to detect and fix something as simple as an absolute value pointing to itself (It is the first time me mentioning that for over 12 years).
I do not have to remember anything anymore thanks to total-recall.

egsuh

  • Hero Member
  • *****
  • Posts: 1533
Re: Why with allows assignment
« Reply #80 on: January 06, 2025, 03:58:27 am »
Quote
But the programming environment has changed. If you now write a method for a form event handler, you are in the context of the TForm class, having hundreds of symbols with very generic names (e.g. name, width, height, top, etc.) and with is used to access other classes which also have hundreds of symbols.
For example, if you use with to access a canvas in the form paint function, a canvas has a width, height, font, etc. All shadowing the properties of the form.

I prefer to use "with" just because of this. I do not think one way is right or wrong, nor one way is better than the other. Simply it's programmer's preference.  For example, when I create a subform within mainform,

           with TSubForm.Create(nil) do begin
                  if ShowModal then begin
                     MainForm.Caption := Edit1.text;   // this line
                  end;
                  Free;
           end;


Without "with",   this line should be  Caption := Subform.Edit1.Text;.    Which one is preferable? I think case by case.


Regarding inside/outside of function result...  I'm not a computer scientist, but I remember I overheard that function results exist only in registers, not in memory --- not sure.  No confusion if function result type is integer etc., but what happens if the function returns record type? If function result is object descendants created within the function then the object's storage is definitely not temporary. Only the address of the object is temporary.  Simply wondering. Curious. 

LV

  • Full Member
  • ***
  • Posts: 206
Re: Why with allows assignment
« Reply #81 on: January 06, 2025, 09:07:49 am »
It is either this or that and not one time this and another time that. But as explained the OP's code obfuscate things probably to such an extend that the compiler is unable to pick up in that.

Nice.

One of Wirth's main principles:

Make it as simple as possible, but not simpler. (A. Einstein)

https://people.inf.ethz.ch/wirth/Oberon/Oberon.Report.pdf

PascalDragon

  • Hero Member
  • *****
  • Posts: 5855
  • Compiler Developer
Re: Why with allows assignment
« Reply #82 on: January 06, 2025, 12:52:10 pm »
BUT, please, do prove me wrong...  find a mainstream compiler that refers to a masked variable as a shadowed variable.

To quote GCC's help:

Quote
-Wshadow

    Warn whenever a local variable or type declaration shadows another variable, parameter, type, class member (in C++), or instance variable (in Objective-C) or whenever a built-in function is shadowed. Note that in C++, the compiler warns if a local variable shadows an explicit typedef, but not if it shadows a struct/class/enum. If this warning is enabled, it includes also all instances of local shadowing. This means that -Wno-shadow=local and -Wno-shadow=compatible-local are ignored when -Wshadow is used. Same as -Wshadow=global.

I think we can agree that GCC is considered a "mainstream compiler"? ;)

But, if you want to argue that, try compiling this:
Code: Pascal  [Select][+][-]
  1. procedure DuplicateIdentifier(Parameter : integer);
  2. var
  3.   Parameter : boolean;
  4. begin
  5. end;
  6.  
Even FPC will tell you there is a duplicate identifier thereby conclusively proving the parameter and the local are in the same scope.

Not really your fault, but to be fair, it's a bad example, cause as far as the implementation in FPC is concerned the parameter and the locals are different symbol tables and the compiler actively needs to check that an identifier inserted into the local symbol table isn't conflicting with one in the parameter symbol table.

Creating a temporary as a result of a "with" statement is absolutely incorrect in all cases.  No exceptions.  Period. The _only_ purpose of a "with" is to establish a scope, nothing more and nothing less.

Some people may support Alice in Wonderland compilers, I'm not one of them.

Then please take your grievances up with the developers at Borland, because Delphi is where this behaviour originates and it is implemented in FPC to follow that behaviour:

Code: Pascal  [Select][+][-]
  1. program twith;
  2.  
  3. {$APPTYPE CONSOLE}
  4.  
  5. var
  6.   cnt: LongInt = 0;
  7.  
  8. type
  9.   TTest = record
  10.     I: LongInt;
  11.   end;
  12.  
  13. function Test: TTest;
  14. begin
  15.   Inc(cnt);
  16.   Result.I := cnt;
  17. end;
  18.  
  19. begin
  20.   with Test do begin
  21.     Writeln(I);
  22.     Writeln(I);
  23.   end;
  24. end.
  25.  

Output (with both Delphi and FPC):

Code: [Select]
1
1

This clearly shows that Test is called once which means that the expressions in the with-clause are only evaluated once and not for each time a member of the corresponding structured type is accessed. If your assumption would be correct then the output would be 1 2 instead.

Oh, and just for the sake of it, replacing the with-statement with the following:

Code: Pascal  [Select][+][-]
  1. with Test, Test do begin


Will result in this output:

Code: [Select]
2
2

BrunoK

  • Hero Member
  • *****
  • Posts: 665
  • Retired programmer
Re: Why with allows assignment
« Reply #83 on: January 06, 2025, 01:26:50 pm »
Code: Pascal  [Select][+][-]
  1. with TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding as LeftPad do
  2. begin
  3.     if LeftPad = 0 then
  4.         LeftPad := 42; // bk corrected syntax error
  5.     X := Y;
  6.     ....
  7. end;
That's already available by defining a 'LeftPad' macro, no need to change the "with" statement to get that funcitonality.
Out of curiosity what would be the syntax for the said ''LeftPad' macro' ?


lainz

  • Hero Member
  • *****
  • Posts: 4685
  • Web, Desktop & Android developer
    • https://lainz.github.io/
Re: Why with allows assignment
« Reply #84 on: January 06, 2025, 01:57:02 pm »
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. uses
  4.  Classes, SysUtils;
  5.  
  6. type
  7.  
  8.   TRecAB = record
  9.     A: integer;
  10.     B: integer;
  11.   end;
  12.  
  13.   { TMyObj }
  14.  
  15.   TMyObj = class
  16.   private
  17.     TRecAB: TRecAB;
  18.   public
  19.     property rec: TRecAB read TRecAB write TRecAB;
  20.   end;
  21.  
  22. var
  23.   Obj: TMyObj;
  24.  
  25. begin
  26.   Obj := TMyObj.Create;
  27.  
  28.   with Obj.rec do
  29.   begin
  30.     A := 10;
  31.     B := 20;
  32.   end;
  33.  
  34.   with Obj.rec do
  35.   begin
  36.     A := 15;
  37.   end;
  38.  
  39.   writeln(Obj.rec.A);
  40.   writeln(Obj.rec.B);
  41.   readln;
  42.  
  43. end.

A is 15 and B is 20 at the end. So it works fine for me...
« Last Edit: January 06, 2025, 02:15:34 pm by lainz »

BrunoK

  • Hero Member
  • *****
  • Posts: 665
  • Retired programmer
Re: Why with allows assignment
« Reply #85 on: January 06, 2025, 01:58:07 pm »
Sadly, it seems that the shown example is syntactically incorrect thus not adding anything constructive to the discussion.

440bx

  • Hero Member
  • *****
  • Posts: 4993
Re: Why with allows assignment
« Reply #86 on: January 06, 2025, 02:13:55 pm »
To quote GCC's help:

Quote
-Wshadow

    Warn whenever a local variable or type declaration shadows another variable, parameter, type, class member (in C++), or instance variable (in Objective-C) or whenever a built-in function is shadowed. Note that in C++, the compiler warns if a local variable shadows an explicit typedef, but not if it shadows a struct/class/enum. If this warning is enabled, it includes also all instances of local shadowing. This means that -Wno-shadow=local and -Wno-shadow=compatible-local are ignored when -Wshadow is used. Same as -Wshadow=global.
Ok, I've been programming for over 40 years and this is the third time I came across the term "shadow" to refer to masking but, fair enough, to my surprise, variable shadowing is a thing.

I think we can agree that GCC is considered a "mainstream compiler"? ;)
Yes, we can agree to that.

Not really your fault, but to be fair, it's a bad example, cause as far as the implementation in FPC is concerned the parameter and the locals are different symbol tables and the compiler actively needs to check that an identifier inserted into the local symbol table isn't conflicting with one in the parameter symbol table.
How FPC deals with its symbol tables is completely irrelevant.  What's relevant is that the parameters and the locals are in the same scope.  Slice it however you like but THAT is a fact and, if you compile that, FPC correctly claims there is a duplicate identifier.  Therefore, regardless of how FPC fiddles with its symbols tables, it reaches the correct conclusion: duplicate identifier, which can only happen when two identifiers are in the same scope. 

Then please take your grievances up with the developers at Borland, because Delphi is where this behaviour originates and it is implemented in FPC to follow that behaviour:
You're right, I just tested it with Delphi 2 and it behaves as you stated.  Another "breakthrough" from the folks that contributed writable constants to computer science. 

Oh, and just for the sake of it, replacing the with-statement with the following:

Code: Pascal  [Select][+][-]
  1. with Test, Test do begin


Will result in this output:

Code: [Select]
2
2
I tried it because I had to see it to believe it and, you're right, that's the output.  It's _appalling_.  oh well... I wonder what N. Wirth would say if he saw this stuff.

One thing you are completely right about, I cannot put the blame on FPC for this disgrace, Delphi initiated it.




@BrunoK,

it would be something like this (untested because I cannot test it):

{$define LeftPad:=TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding}

and, IMO, it would be better to name the macro "LeftPadding" instead of "LeftPad" as this would make it more evident that the field being referenced is "LeftPadding".
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

BrunoK

  • Hero Member
  • *****
  • Posts: 665
  • Retired programmer
Re: Why with allows assignment
« Reply #87 on: January 06, 2025, 02:43:51 pm »

@BrunoK,

it would be something like this (untested because I cannot test it):

{$define LeftPad:=TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle.LeftPadding}

and, IMO, it would be better to name the macro "LeftPadding" instead of "LeftPad" as this would make it more evident that the field being referenced is "LeftPadding".

My comments :

1° DBannon's example is syntactically incorrect.

Re 440bx macro.
2° Macro adds complexity in this case.
3° Using macro means that TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle will be evaluated twice at runtime whereas the with will evaluate the complete TKMemoParagraph(KMemo1.Blocks[ABlockNo]).Parastyle once and use directly the reference to that field TWICE .

In the case of multiple and deeply referenced fields, it can mean much improved response times at runtime. The sample app (https://forum.lazarus.freepascal.org/index.php/https://forum.lazarus.freepascal.org/index.php/topic=69755.msg542480#msg542480 working !) I posted before contains quite a few WITH usage, please criticize the code / ask for explanations about the code.

Code: Pascal  [Select][+][-]
  1. unit frmWith;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, BufDataset, DB, Forms, Controls, Graphics, Dialogs,
  9.   DBGrids;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     BufDataset1: TBufDataset;
  17.     DataSource1: TDataSource;
  18.     DBGrid1: TDBGrid;
  19.     procedure FormActivate(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.FormActivate(Sender: TObject);
  36. var
  37.   i: integer;
  38.   lFI1, lFI2: TIntegerField;
  39. begin
  40.   with DBGrid1.DataSource, DataSet do
  41.     if not Active then begin
  42.       DisableControls;
  43.       with FieldDefs do begin
  44.         Clear;
  45.         FieldDefs.Add('Auto', ftAutoInc);
  46.         FieldDefs.Add('I1', ftInteger);
  47.         FieldDefs.Add('I2', ftInteger);
  48.       end;
  49.       TBufDataset(DataSet).CreateDataset;
  50.       Active:=True;
  51.       lFI1 := TIntegerField(FieldByName('I1'));
  52.       lFI2 := TIntegerField(FieldByName('I2'));
  53.       for i := 0 to 9 do begin
  54.         Append;
  55.         lFI1.AsInteger := i;
  56.         lFI2.AsInteger := i;
  57.         Post;
  58.       end;
  59.       EnableControls;
  60.     end;
  61. end;
  62.  
  63. end.

440bx

  • Hero Member
  • *****
  • Posts: 4993
Re: Why with allows assignment
« Reply #88 on: January 06, 2025, 04:49:10 pm »
the "with" implementation in Delphi and FPC is an appalling atrocity, it is _irrelevant_ if that atrocity may be convenient to someone, it is wrong.  Just as wrong as the existence of writable constants.

It seems that appalling "with" implementation is to sidestep a performance problem in OOP.  As usual, one bad idea leads to another.

Fortunately, the "with" statement works mostly as expected in the absence of OOP. "Mostly" because the OP showed an example where "with" doesn't work as it should and there is no OOP in the example.

All that said, if you or anyone else wants to suggest adding "as" to the "with" statement, be my guest, it is something that will never be in my code (just like the rest of OOP.)
(FPC v3.0.4 and Lazarus 1.8.2) or (FPC v3.2.2 and Lazarus v3.2) on Windows 7 SP1 64bit.

dbannon

  • Hero Member
  • *****
  • Posts: 3212
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Why with allows assignment
« Reply #89 on: January 07, 2025, 06:21:04 am »
Sadly, it seems that the shown example is syntactically incorrect thus not adding anything constructive to the discussion.

I am not sure how you came to that conclusion Bruno. The first example I showed compiles and behaves exactly as I expect. The second example, based on what Bart mentioned was a "proposal" would, IMHO be correct, would compile and would run as expected if it was implemented. Obviously, it has not been implemented so, bit hard to test right now.

You suggest my example is "syntactically incorrect" in two separate posts, please elaborate ? A typo ? Possible but irrelevant.

@440bx, yes, same thing could be achieved with Macros but quite untidy code IMHO. In the case of my code, I have many identifiers similar to but usually slightly different, so, one macro would not suffice, multiple macros would start to look like C Code , yek !
 
Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

 

TinyPortal © 2005-2018