Jump to content
Yaron

With's the deal with "With"?

Recommended Posts

Using "with" can clean up the code to make it easier to read, but ever since the dawn of Delphi, it had a severe downside that "with" hinders debugging.

You can't simply hover with the mouse cursor over a variable to get its value or use evaluate/modify on the variable itself without having to copy & paste the record/class name first.

 

And now I've encountered a more recent issue with refactoring. If you use refactoring to rename a variable inside a record, it will miss any instances where the record was used with a "with" statement.

 

Will this ever be improved?  If not, what are your recommendations?

Edited by Yaron
  • Like 1

Share this post


Link to post

My recommendation is simple: Don't use the WITH keyword.

 

There are not just the problems you mention. It can also be difficult to determine the scope of an identifier within a WITH block. Example:

var
  Caption: char;

procedure TMyForm.Button1Click(Sender: TObject);
type
  TSomeRec = record
    Caption: integer;
  end;
var
  Caption: double;
  SomeRec: TSomeRec;
begin
  // [lots of code]
  with SomeRec do begin
    // [lots of code]
    Caption := 'bla'; // <- which Caption variable/property/field is accessed here? And why does it not compile?
  end;
end;

We have had this discussion so many times that it is really pointless to start it afresh. Just google it.

  • Like 1

Share this post


Link to post

I'm aware of past discussion with regards to debugging, I was just not aware that it also broke refactoring which is why I started the topic.

 

Was there ever an official reply on an intent to resolve some of these issues?

Share this post


Link to post

I'm guessing they are discussing to drop the with statement altogether once a new release is on the horizon, but chicken out in the last minute. So nothing is done to fix anything broken by with.

Share this post


Link to post

Debugging was never the issue. That was just annoying. Refactoring was never the issue either. That also is annoying but one of many broken aspects of the refactoring. 

 

The issue is the silent bugs that arise when you add a field to a type and that one hides local variables.

 

This has been discussed hundreds of times. Try some more websearch. 

 

Then, stop using with. 

  • Like 4

Share this post


Link to post

Regarding a fix, this is only one of a number of problems with scope resolution in the language.

 

Same issue arises when you use units, where more recently imported symbols hide those from units imported earlier. Same for local variables and methods. Same for local variables and global symbols. 

 

Anywhere where you refer to a symbol and there are multiple matches then the compiler chooses the most recent. 

 

In princple the resolution is simple. Introduce a new compiler warning whenever the compiler uses this most recent principle. 

  • Like 5

Share this post


Link to post
43 minutes ago, David Heffernan said:

In principle the resolution is simple. Introduce a new compiler warning whenever the compiler uses this most recent principle. 

+1000

 

And then also introduce proper namespacing and ways to alias things (not only non generic types) at the consuming side (for example)

Edited by Stefan Glienke
  • Like 3

Share this post


Link to post

Yes, please!
proper namespacing like in Remobjects.Elements would be ...........

  • Like 2

Share this post


Link to post

As everything powerful can be armful when misused, I like "with", when it is not over-abused.

For instance, I like "with", when it has only several lines of scopes. It does make sense to me, when used for a single line.
Otherwise, I switch to an implicit local variable: asm generation would be the very same.

 

If you think "with" should be avoided due to its implicit context switch, I guess next step would be to force using self.foo for class properties... just like the $this->foo of Php... what a progress! 🙂
I am against any new warning at compiler level.
And if you are confused by with someclass do x := a * b and don't know to which class a, b, or x belongs to, it is time renaming the properties/functions to something more explicit!

Edited by Arnaud Bouchez
  • Thanks 1
  • Confused 1
  • Sad 1

Share this post


Link to post
37 minutes ago, Arnaud Bouchez said:

And if you are confused by with someclass do x := a * b and don't know to which class a, b, or x belongs to, it is time renaming the properties/functions to something more explicit!

It's not about proper naming - its about newly added members creeping into the with scope - as happened when TRect got Height and Width properties!

It was perfectly clear that Top, Left, Right, Bottom referred inside the with belonged to the "withed" rect variable and Height and Width to something outside - now those got added to TRect and boom.

Edited by Stefan Glienke
  • Like 6

Share this post


Link to post
1 hour ago, Arnaud Bouchez said:

And if you are confused by with someclass do x := a * b and don't know to which class a, b, or x belongs to, it is time renaming the properties/functions to something more explicit!

I beg to differ. Naming a property/function more explicitly for the sake of with is...how do I put this politely?...not helpful. Say I have a class TCar with properties Wheel and Engine and functions Drive and Park. If I understand you correctly you would expect me to call those properties and functions CarWheel, CarEngine, CarDrive and CarPark, to be explicit enough for the rare case when someone uses with on my TCar...

  • Like 3

Share this post


Link to post
4 hours ago, Stefan Glienke said:

It was perfectly clear that Top, Left, Right, Bottom referred inside the with belonged to the "withed" rect variable and Height and Width to something outside - now those got added to TRect and boom.

Breaking compatibility by adding properties is very likely to break code...
My assumption is that you use and maintain your own classes.

 

3 hours ago, Sherlock said:

Naming a property/function more explicitly for the sake of with is not helpful.

This was not my point. I didn't mean CarWheel for sure. Wheel, Engine and Drive are fine. a, b and x aren't.
And I wouldn't use "with" over two TCar instances, or two classes with a Wheel property.
But it would have sense, say e.g. to write something like:

with currentCar do
  CarInfo.Caption := format('Engine %s with %d wheel(s)', [Engine.ModelName, Wheel.Count]);

My point was that "As everything powerful can be armful when misused, I like "with", when it is not over-abused".
If the "with" scope is small enough to be obvious and safe, fine. Otherwise, use local variables.

  • Like 1

Share this post


Link to post
59 minutes ago, Arnaud Bouchez said:

Breaking compatibility by adding properties is very likely to break code...
My assumption is that you use and maintain your own classes.

We are talking about TRect here. Very basic type used all over. 

It is not very likely that one will create his own types for just about everything. And this is where using with can easily break your code without you even realizing it is broken.

This is especially problematic when used in combination with UI where testing is hard and you can have a lot of code that is poorly covered with tests that could reveal such issues.

 

'with' is relic of another time and another coding practices. And, yes you could shoot yourself in the foot even back then, but now it is extremely easy to do so. Not using 'with' is the best advice one could offer. It is almost like using goto and absolute. You may have some very limited use case where using it is justified, but you will not find such constructs being used at large.

  • Like 5

Share this post


Link to post

What is sure with "with" discussion is that it is a trolling subject in the Delphi community since decade(s).

For instance, the topic is about "with" in general, and that @Yaron stated that the IDE debugger has troubles with it.
TRect came as a true problem about "with" - I do agree with Stefan. But problem came from Embarcadero, not taking into account existing code base. Just like ARC, or the AnsiStrings...

 

Most dev people outside this forum would argue, paraphrasing @Dalija Prasnikar,  that 'Delphi is a relic of another time and another coding practices: for instance, there is no GC !'. 
Php adepts would argue that writing $this->foo is much cleaner/safer than what Delphi (and other languages) do about properties.


I like "with" - when properly used for two-liners code - as I wrote. It is a clean way of writing code, "making it easier to read" as wrote Yaron.
Similarly, I like "object" more than "record with methods", since it allows easy inheritance - when using high-performance code with pointers and pre-allocated memory buffers/arrays. Of course, with only static methods.
This was just my 2 cents, to introduce another POV into the discussion. Nothing is totally black nor white.

Edited by Arnaud Bouchez
  • Like 2

Share this post


Link to post
31 minutes ago, Dalija Prasnikar said:

'with' is relic of another time and another coding practices. And, yes you could shoot yourself in the foot even back then, but now it is extremely easy to do so. Not using 'with' is the best advice one could offer. It is almost like using goto and absolute. You may have some very limited use case where using it is justified, but you will not find such constructs being used at large.

The inherent dangers of 'with' are at their worst with such usage as: with UnitA, UnitB do.

Share this post


Link to post
3 minutes ago, Arnaud Bouchez said:

@Bill Meyer
Is   with UnitA, UnitB do   even suppose to compile?

Oh, yes. you can pass 1..n units. Of course, what you may get, with name collisions -- which will not be reported -- is up for grabs. (Note that FixInsight now checks for with clauses which use more than one unit.)

Edited by Bill Meyer
  • Like 1

Share this post


Link to post

with SomeUnitName do   doesn't compile on my Delphi 10.3 (nor FPC).
Compiler complains with

[dcc64 Error] Test.dpr(200): E2029 '.' expected but 'DO' found

  • Confused 1

Share this post


Link to post
9 minutes ago, Arnaud Bouchez said:

What is sure with "with" discussion is that it is a trolling subject in the Delphi community since decade(s).

This is rather sad fact(s). Both that talking about bad coding practices is considered trolling as well as fact that after of decades of discussions and knowing that certain code is bad it is still widely used and all warnings are ignored.

 

9 minutes ago, Arnaud Bouchez said:

TRect came as a true problem about "with" - I do agree with Stefan. But problem came from Embarcadero, not taking into account existing code base. Just like ARC, or the AnsiStrings...

Embarcadero cannot possibly take in account every single line of code out there. Problem here is inherently in 'with' and not in adding additional functionality. Every change has potential to break some code somewhere, but with attitude that nothing should be changed we would have no progress. That does not mean that bad code changes will not happen.

9 minutes ago, Arnaud Bouchez said:

Most dev people outside this forum would argue, paraphrasing @Dalija Prasnikar,  that 'Delphi is a relic of another time and another coding practices: for instance, there is no GC !'. 
Php adepts would argue that writing $this->foo is much cleaner/safer than what Delphi (and other languages) do about properties.

We are not discussing other languages here. 'with' is the problem within language itself.

9 minutes ago, Arnaud Bouchez said:

I like "with" - when properly used for two-liners code - as I wrote. It is a clean way of writing code, "making it easier to read" as wrote Yaron.

You can shoot yourself in the foot with 'with' even in single line of code. Again, main problem is that code may break due to far away changes you may not even be aware of. 'with' makes code fragile and prone to breakage more than it would be without it. Any code can be broken one way or another. Point is minimizing accidental and subtle breakage as much as possible.

9 minutes ago, Arnaud Bouchez said:

This was just my 2 cents, to introduce another POV into the discussion. Nothing is totally black nor white.

On this we agree. With every rule there are few exceptions. That does not mean we should not strive to educate about particular code dangers, especially when benefits are minimal or non existent and can be obtained by using different techniques.

  • Like 2

Share this post


Link to post
5 hours ago, Arnaud Bouchez said:

Php adepts would argue that writing $this->foo is much cleaner/safer than what Delphi (and other languages) do about properties.

Guess what - there are even rules that will tell you when you access things without using this in static code analyzers - and there are good reasons to do so (although I personally dislike it)

 

Speaking of programming language design - you never can make everything perfect and there are always features that have some "be careful with it!" tags on them. However features that are so often accused of causing problems or confusions can be declared bad. And yes, every language has them and every responsible Developer should know when it safe to use them.

 

Fun fact: I just recently consciously used goto because it yielded a noticable performance gain and rewriting the code without it would have been a massive undertaking resulting in more complicated code than just putting a goto there. And no, no raptor attacked me (yet...)

Edited by Stefan Glienke
  • Like 3
  • Haha 1

Share this post


Link to post
28 minutes ago, Stefan Glienke said:

Fun fact: I just recently consciously used goto because it yielded a noticable performance gain and rewriting the code without it would have been a massive undertaking resulting in more complicated code than just putting a goto there. And no, no raptor attacked me (yet...)

This subject came up yesterday at the office. I could not recall what topic it was in Knuth's magnum opus where he explains that although goto can always be avoided, there are rare cases in which its use brings performance benefits. Seems like a sorting discussion. Quicksort?

Share this post


Link to post

This is a quote from Knuth's C adaptation of the original Adventure game by Will Crowther:

Quote

By the way, if you don't like goto statements, don't read this. (And don't read any other programs that simulate multistate systems.) 

 

  • Like 2

Share this post


Link to post

Regardless of whether using WITH is a good idea, there IS a solution ... The Convert WITH expert in MMX. It's not perfect and I always take the step of DUPLICATING the block I'm playing with and commenting out one copy, but the expert does a VERY GOOD JOB MOST OF THE TIME, in converting WITH's to object-referenced individual lines. I know, because I was one of (if maybe not the first) the early requestors for this expert. I'm a self-admitted WITH user. I find it easier on my eyes because I indent more than most. Albeit only two spaces per indent.

 

With Uwe doing such a great job keeping MMX alive ... and for a most reasonable price 😉, whether you use it as a crutch early in development or go back through on refactoring passes, the decision to use WITH is only an installation of MMX from being made to go away.

  • Like 1

Share this post


Link to post

Regardless of is WITH statement good or is it bad it's a part of language and debugging, refactoring and other code tools should be aware of it.

As a bonus, it may also hint which identifiers come from which scope :classic_wink:

withHighlight.thumb.gif.00bd56bff3e14dbdaee8376b83493b97.gif

  • Like 2

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×