Recent

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

Warfley

  • Hero Member
  • *****
  • Posts: 2067
Re: Why with allows assignment
« Reply #60 on: January 05, 2025, 04:01:13 pm »
With as it was originally is one of these weird features that just shortens typing without enabling any new way of doing things. Simultaneously it introduced shadowing. This was fine in the context where it was conceived, in procedural programs used in functions with a few dozens local and global variables, to access record variables with a single to low double digit number of fields. There shadowing isn't a problem.

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.

So while with wasn't that much of an issue in the code bases for which it was developed, today it does more harm than good. So in the very most situations I would recommend to just leave with as a relic of the past and not to use it.

That said, with introduces an interesting new concept to Pascal, scope. One example for this is the usage with class instances, as shown already here
Code: Pascal  [Select][+][-]
  1. with TMyClass.Create do
  2. try
  3.   ...
  4. finally
  5.   Free;
  6. end;
This allows something more than just avoiding to write out some code, it specifically limits usable scope of the created object, avoiding things like use after free errors and syntactically binding the try finally to the creation in the with block.
This is an actual benefit to the language.

The only downside is that with is not scoped with respect to lifetime, the temporary object created for with has the same lifetime as the function which is why it's not really usable for managed types. I tried to change it in this MR, but it's not yet finished: https://gitlab.com/freepascal.org/fpc/source/-/merge_requests/860

So IMHO the creation of a scoped temporary object is pretty much the only reason why with is actually useful. Since code completion is a thing, there is no reason to have to avoid writing out a few more characters, so using with just to shorten code is a bad practice

Bart

  • Hero Member
  • *****
  • Posts: 5731
    • Bart en Mariska's Webstek
Re: Why with allows assignment
« Reply #61 on: January 05, 2025, 07:00:35 pm »
There is/was a proposal for something like:
Code: [Select]
  with VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC as myvar do
  begin
    myvar.somefield := somevalue;
   ...
  end;

That could make sense to me (in order to not having to type VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC over and over again, and at the same time not having scope issues (not knowing wether e.g Width points to VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC or to e.g. the form it is in.

Bart

Warfley

  • Hero Member
  • *****
  • Posts: 2067
Re: Why with allows assignment
« Reply #62 on: January 05, 2025, 07:11:35 pm »
There is/was a proposal for something like:
Code: [Select]
  with VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC as myvar do
  begin
    myvar.somefield := somevalue;
   ...
  end;

That could make sense to me (in order to not having to type VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC over and over again, and at the same time not having scope issues (not knowing wether e.g Width points to VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC or to e.g. the form it is in.

Bart
The as Syntax is not working, because as expressions are already allowed in with statements. But yeah if you look at the MR I've linked above it can do:
Code: Pascal  [Select][+][-]
  1. with const fl = openfile(filename, fmWrite) do
  2.   fl.Write(42);
  3.  
Specifically ensuring that the finalizer of fl is called after the with block (and in this example the file handle would be freed)

440bx

  • Hero Member
  • *****
  • Posts: 6542
Re: Why with allows assignment
« Reply #63 on: January 05, 2025, 07:22:16 pm »
With as it was originally is one of these weird features that just shortens typing without enabling any new way of doing things.
"with" is just a way to establish a scope.   Nothing "weird" about it. 

Simultaneously it introduced shadowing.
Shadowing ???  what are you talking about ?  and where did you get that stuff from ?  references please!.

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.
and magically, somehow, abracadabra... now it is possible to assign a value to a function result _outside_ of the function.  The computer science field needed that about as much as writable constants.  Welcome to Alice in Wondercode.

So IMHO the creation of a scoped temporary object is pretty much the only reason why with is actually useful. Since code completion is a thing, there is no reason to have to avoid writing out a few more characters, so using with just to shorten code is a bad practice
There is absolutely nothing wrong about using "with" but, there are obviously programmers who are totally clueless about how to use it correctly but, why would anyone care about that ? (just in case, that's rethorical.)
« Last Edit: January 05, 2025, 07:24:49 pm by 440bx »
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Warfley

  • Hero Member
  • *****
  • Posts: 2067
Re: Why with allows assignment
« Reply #64 on: January 05, 2025, 08:23:18 pm »
Shadowing ???  what are you talking about ?  and where did you get that stuff from ?  references please!.
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = record
  3.     x: Integer;
  4.   end;
  5.  
  6. procedure Foo(x: Integer);
  7. var
  8.   t: TTest;
  9. begin
  10.   with t do
  11.     x := 42;  // t.x shadows param x
  12. end;

Shadowing means overriding some identifier with another one. Sometimes shadowing can be disambiguated, e.g. when one unit shadows definitions of another unit you can write unit1.x to disambiguate.
Free Pascal does not allow shadowing without disambiguation, e.g. the following is not allowed.for.exactly that reason:
Code: Pascal  [Select][+][-]
  1.  procedure foo(x: Integer);
  2. var
  3.   x: Double;
  4. begin
  5. end;
With statements allow to shadow symbols without disambiguation (as in the example above), which is why it's bad. It's so bad that languages that used to have with, like JavaScript, deprecated it, because it's a syntactic concept that's born out of pure laziness, gives no semantic benefits but due to shadowing can introduce errors.

Quote
and magically, somehow, abracadabra... now it is possible to assign a value to a function result _outside_ of the function.  The computer science field needed that about as much as writable constants.  Welcome to Alice in Wondercode.
Imagine a world in which you can't change any result of a function, then the following would also not work:
Code: Pascal  [Select][+][-]
  1. t := MyFunc;
  2. t.x := 42;
In this example t is the result of a function but is changed outside the function. Oh no hell breaks lose, how can this be acceptable!!!!

There is literally no difference between:
Code: Pascal  [Select][+][-]
  1. t := MyFunc;
  2. with t do
  3.   x := 42;
  4. // And
  5. with MyFunc do
  6.   x := 42;
Just that in the first example the object where the result is written I to has an explicit name, while in the second one it's the result of an anonymous expression.

Just because it doesn't have a name doesn't mean it doesn't exist.

But I mean you do not know what shadowing is, which is absolutely basic knowledge about programming language design, so I guess there is no real reason to continue to argue

tetrastes

  • Hero Member
  • *****
  • Posts: 768
Re: Why with allows assignment
« Reply #65 on: January 05, 2025, 09:05:32 pm »
Following this logic consistently, there must be literally no difference between:

Code: Pascal  [Select][+][-]
  1. t := MyFunc;
  2. t.x := 42;
  3. // And
  4. MyFunc.x := 42;

but this is not so. And this inconsistency is the problem with all that stuff.
As they say, either take off the cross or put on your underpants.  :D

440bx

  • Hero Member
  • *****
  • Posts: 6542
Re: Why with allows assignment
« Reply #66 on: January 05, 2025, 09:08:24 pm »
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = record
  3.     x: Integer;
  4.   end;
  5.  
  6. procedure Foo(x: Integer);
  7. var
  8.   t: TTest;
  9. begin
  10.   with t do
  11.     x := 42;  // t.x shadows param x
  12. end;

Shadowing means overriding some identifier with another one. Sometimes shadowing can be disambiguated, e.g. when one unit shadows definitions of another unit you can write unit1.x to disambiguate.
First, what you are calling "shadowing" is normally referred to as _masking_. 

Free Pascal does not allow shadowing without disambiguation, e.g. the following is not allowed.for.exactly that reason:
Code: Pascal  [Select][+][-]
  1.  procedure foo(x: Integer);
  2. var
  3.   x: Double;
  4. begin
  5. end;
With statements allow to shadow symbols without disambiguation (as in the example above), which is why it's bad.
You really need to learn basic compiler construction theory.  In that code there is no masking (or shadowing as you call it), what there is, is a _duplicate_ identifier. No correctly implemented compiler would allow that code because the parameter "x" and the local variable "x" are in the _same_ scope, therefore they cannot use the same name. 

It's so bad that languages that used to have with, like JavaScript, deprecated it, because it's a syntactic concept that's born out of pure laziness, gives no semantic benefits but due to shadowing can introduce errors.
Yes, allowing duplicate identifiers is so bad that no correctly implemented compiler allows it.  Duplicate identifiers have been "deprecated" since the birth of compilers and interpreters.


Imagine a world in which you can't change any result of a function, then the following would also not work:
Code: Pascal  [Select][+][-]
  1. t := MyFunc;
  2. t.x := 42;
In this example t is the result of a function but is changed outside the function. Oh no hell breaks lose, how can this be acceptable!!!!
Nice try but "t" is most definitely NOT the result of a function.  "t" is where the result of the function is copied to.  That's VERY different.  The result of the function is NOT being changed, the location where the result of the function has been COPIED to is being changed.

OTH, FPC is allowinjg this atrocity (which you are trying to hide with the above code which is very different):
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. type
  4.   TTest = record
  5.     V: Integer;
  6.   end;
  7.  
  8. var
  9.   X: TTest;
  10.  
  11.   function Test: TTest;
  12.   begin
  13.     Result := X;
  14.   end;
  15.  
  16. var
  17.   T: TTest;
  18. begin
  19.   with Test do
  20.     V := 1; //No Error
  21.   Test.V := 1; //Error: Argument cannot be assigned to
  22. end.                
Line 20 is _changing_ the result of the function because line 20 is semantically identical as line 21 but FPC _incorrectly_ considers them different which is an _atrocity_.

But I mean you do not know what shadowing is, which is absolutely basic knowledge about programming language design, so I guess there is no real reason to continue to argue
Let's engage in "basic knowlege"... it's not called "shadowing" (at least not by those who know a little compiler theory), it's called masking.  if you need confirmation of that, write a C program that masks one variable or parameter and you'll get a hint or warning telling you that some variable is _masking_ another one. 

BUT, please, do prove me wrong...  find a mainstream compiler that refers to a masked variable as a shadowed variable.

I _strongly_ suggest you learn basic compiler construction theory.  I highly recommend the book "Per Brinch Hansen On Pascal Compilers" (unfortunately, that book does not cover the "with" statement), another really good educational compiler is Per Brinch Hansen's SuperPascal (source available online) and I also strongly recommend you read "The P4 Compiler and Interpreter
by Steven Pemberton, Amsterdam, and Martin Daniels", text is available onlne at:
http://pascal.hansotten.com/uploads/pemberton/homepages.cwi.nl/_steven/pascal/book/pascalimplementation.html

After you've learned basic compiler theory, we can resume the discussion about the "with" statement. 

Have fun reading and please learn.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

Warfley

  • Hero Member
  • *****
  • Posts: 2067
Re: Why with allows assignment
« Reply #67 on: January 05, 2025, 09:40:46 pm »
First, what you are calling "shadowing" is normally referred to as _masking_.
No it's not: https://en.m.wikipedia.org/wiki/Variable_shadowing

Quote
You really need to learn basic compiler construction theory.  In that code there is no masking (or shadowing as you call it), what there is, is a _duplicate_ identifier. No correctly implemented compiler would allow that code because the parameter "x" and the local variable "x" are in the _same_ scope, therefore they cannot use the same name.
No it's not, because I know about basic Compiler construction and know that parameters are in their own symtable and there by spawn their own scope. In the FPC sources it's called the paramst.

But if you want another example:
Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = class
  3.     I: Integer;
  4.     procedure foo(I: Integer);
  5.   end;
Also doesn't work (at least in mode ObjFpc) because it forbids shadowing

Quote
Nice try but "t" is most definitely NOT the result of a function.  "t" is where the result of the function is copied to.  That's VERY different.  The result of the function is NOT being changed, the location where the result of the function has been COPIED to is being changed.
First, technically speaking, anything to big for a register will be passed by reference, so the function literally constructs the result in place. But even putting that technicality aside: congratulations you exactly got the point I was making all along. When you use with, you don't change the result of the function, you change a temporary object where the result of the function is assigned to

So thank you for agreeing with me. So what's your problem?

440bx

  • Hero Member
  • *****
  • Posts: 6542
Re: Why with allows assignment
« Reply #68 on: January 05, 2025, 10:38:01 pm »
No it's not: https://en.m.wikipedia.org/wiki/Variable_shadowing
Interesting... second time (first was you) I see it referred to as shadowing.  Find a compiler that emits a message stating that some variable is "shadowing" another one... good luck but, you can find a compiler that warns about a variable masking another one (that's what every C compiler I've used emitted as a message.)

All that said, I admit you found a respectable place that refers to it as shadowing.  Now find a compiler that does (just in case, the word shadow or shadowing must appear in the compiler message or documentation.)

No it's not, because I know about basic Compiler construction and know that parameters are in their own symtable and there by spawn their own scope. In the FPC sources it's called the paramst.
I couldn't care less how FPC implements its scope resolution mechanism but that comment proves that you know less about compiler construction than the Pope knows about quantum mechanics.  Locals and parameters are in the same scope which is why locals cannot have the same name as parameters because that would cause a duplicate identifier.

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.

As I suggested in the previous post, you need to read a few books about compiler construction.

So thank you for agreeing with me. So what's your problem?
I couldn't possibly agree with you.  I don't have a problem but, I suggest, again, you read the books I previously mentioned.

FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Why with allows assignment
« Reply #69 on: January 05, 2025, 10:49:36 pm »
Not saying I have any knowledge at all but for sure when reading this kind of arguments one could become confused pretty fast.

imho you are both saying exactly the same only using other words and viewing it from another standpoint/angle (each with their own believes). More problematic is clouding the topic with objects which wasn't part of the original question to begin with.

At one time in history it worked both ways (assigning and accessing) and now it doesn't anymore because of the copy (or so called shadow) thus end of the story: don't use it unless knowing what you're doing. Don't use it in case you' don't like it and use it case you do like it  *period*.
Today is tomorrow's yesterday.

440bx

  • Hero Member
  • *****
  • Posts: 6542
Re: Why with allows assignment
« Reply #70 on: January 05, 2025, 11:15:14 pm »
imho you are both saying exactly the same only using other words and viewing it from another standpoint/angle (each with their own believes).
No, what he is saying is very different than what I'm saying and totally opposite of what a correctly implemented compiler would do.

More problematic is clouding the topic with objects which wasn't part of the original question to begin with.
You got that part right.

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.
FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

TRon

  • Hero Member
  • *****
  • Posts: 4377
Re: Why with allows assignment
« Reply #71 on: January 05, 2025, 11:38:19 pm »
No, what he is saying is very different than what I'm saying and totally opposite of what a correctly implemented compiler would do.
What I see/read and believe that matters is the end result which is the same. Aside whether or not that is correct behaviour for a compiler.

Quote
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.
If I recall correctly that was a decision made for FPC by being Delphi compatible (which thus changed that behaviour).

And fwiw I do agree with you that there is a problem with regards to scope... but that is for the fine gents to figure out once the anonymous functions gets implemented/used. Without properly addressing scopes anonymous functions can be buried into the landfill of unusability.
Today is tomorrow's yesterday.

440bx

  • Hero Member
  • *****
  • Posts: 6542
Re: Why with allows assignment
« Reply #72 on: January 05, 2025, 11:56:47 pm »
What I see/read and believe that matters is the end result which is the same. Aside whether or not that is correct behavior for a compiler.
But the end result isn't the same.  The result is that it is possible to assign a value to a function outside the function which is not just incorrect, it's appalling.  see the OP's code (first post), one statement is accepted while the other isn't in spite of the fact that the two are supposed to be semantically identical.

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 ?

The list of problems that incorrect behavior creates is long.

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.


FPC v3.2.2 and Lazarus v4.0rc3 on Windows 7 SP1 64bit.

LV

  • Sr. Member
  • ****
  • Posts: 427
Re: Why with allows assignment
« Reply #73 on: January 05, 2025, 11:59:14 pm »
In comparison to this topic, quantum dynamics appears to be more comprehensible.   :o

dbannon

  • Hero Member
  • *****
  • Posts: 3826
    • tomboy-ng, a rewrite of the classic Tomboy
Re: Why with allows assignment
« Reply #74 on: January 06, 2025, 12:26:46 am »
Thanks Bart, a useful contribution to this thread.

There is/was a proposal for something like:
Code: [Select]
  with VarWithVeryLongNameSoLongThatItAlmostFeelsLikeYoureUsingC as myvar do
  begin
    myvar.somefield := somevalue;
   ...
  end;

That would be good if it accepted a 'qualified' variable name, eg

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

EDIT : fixed a typo that, apparently, Bruno could not identify as such. Sorry.
« Last Edit: January 08, 2025, 09:43:25 am by dbannon »
Lazarus 4, 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