Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.

Public Report
Report From: Delphi-BCB/Compiler/Delphi/Language    [ Add a report in this area ]  
Report #:  679   Status: Open
The infamous WITH resolution
Project:  Delphi Build #:  Next Version
Version:    6.0 Submitted By:   Martin Kammann
Report Type:  Suggestion / Enhancement Request Date Reported:  4/10/2002 10:57:33 PM
Severity:    Commonly encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   79130
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: (126 Total Ratings)
4.13 out of 5
Total Votes: 270
Description
We all know what difficulties, dangers and ambiguities are involved in using the WITH statement.

I for one try to avoid it wherever possible, but there are some instances, in which it is very handy.

So this is what I propose: in analogy to the
on E:Exception ...
statement I would like to have a

with S:TStringList.Create do
try
  Add('A');
  Add('B');
  CallAProcedure(S); // here it's really of great use!
finally
  S.Free; // S could also be ommitted here
end;

As a side effect this could also help code insight to resolve identifiers within a with block.

More in Details -->
Steps to Reproduce:
Just another example for its use:


var
  MyForm: TForm;
  MyLabel: TLabel;
begin
  ...
  with a:MyForm, b:MyLabel do
  begin
    A.Caption := 'Hello World'; // a simple Caption :=... would be ambiguous to the human reader
    B.Visible := True;  // same here
  end;
  ..
end;
Workarounds
None
Attachment
None
Comments

Sebastian Modersohn at 4/11/2002 5:54:08 AM -
I agree with your first example but the second one seems rather pointless. What's the difference between

var
  MyForm: TForm;
  MyLabel: TLabel;
begin
  ...
  with a:MyForm, b:MyLabel do
  begin
    A.Caption := 'Hello World'; // a simple Caption :=... would be ambiguous to the human reader
    B.Visible := True;  // same here
  end;
  ..
end;

and

var
  MyForm: TForm;
  MyLabel: TLabel;
begin
  ...
  MyForm.Caption := 'Hello World';
  MyLabel.Visible := True;  
  ..
end;

Rod Rishworth at 4/11/2002 6:36:00 AM -
The second example as such may be a little pointless, but the value of aliasing is not. I tend to give my variables long descriptive names, sometimes 30-40 characters long (these are the exception but over 20 is not uncommon - it only takes two long words).

A better example might be:

  with S: SomeLongIdentifierWhichIsTheSource, D: AnotherLongIdentifierWhichIsTheDestination do
  begin
    D.Name := S.Name;
    D.Value := S.Value;
    // etc
  end;

Instead of:

  AnotherLongIdentifierWhichIsTheDestination.Name := SomeLongIdentifierWhichIsTheSource.Name;
  AnotherLongIdentifierWhichIsTheDestination.Value := SomeLongIdentifierWhichIsTheSource.Value;

Also - and I know it is a little naughty - we Delphi programmers tend to use a line of properties:

  MyForm.StatusBar.Panels[0].Text := MyForm.Memo.Font.Name;

If we are also going to fill Panels[1] with IntToStr(Size) etc, each line is going to be pretty long without withs.

And that is of course the point, we go without the with anyway, because if the code is as follows:

  with MyForm.StatusBar, MyForm.Memo.Font do
     Panels[0].Text := Name;

and we hang the mouse over the Name token, what do we see? 'MyForm', most likely - not 'Arial' or whatever.
Sure this could be fixed - its a bug I guess, but the exception style syntax proposed is elegant and would promote good programming practice.

The with alias statement is just like the aliasing in SQL (select a.field from longertablename a).

Essentially what is happening of course is that you are declaring an implicit variable with block scope, one of the few C features I miss in pascal; this syntax would make the implicit with explicit. I'd even go so far as to say that the old style should be deprecated, so that bugs such as the following can not occur:

with MyQuery, MyForm do
begin
  // ...
  Close;
end;

which of course closes the query (actually when I made this error it was a Paradox table lol).

In summary, the request could make the with statement usable again, silly examples notwithstanding

Craig Young at 8/26/2003 8:14:54 AM -
>Also - and I know it is a little naughty - we Delphi programmers tend to use a line of properties:
>
>  MyForm.StatusBar.Panels[0].Text := MyForm.Memo.Font.Name;

Yes, this is very naughty, and I've heard thousands of excuses for it.  The only one that is remotely justifiable is the fact that the design of the VCL classes are the cause of the problem.

However,  I don't think this means we should start using the with statement.  (I know this is opening myself up for a flaming, but I would sooner vote that the with clause be removed entirely.)

> ... but the exception style syntax proposed is elegant and would promote good programming practice.

I disagree, bearing in mind your earlier comment, I feel this change would actually promote a lazier approach to objected oriented design.

Sebastian Modersohn at 4/11/2002 9:41:19 AM -
>In summary, the request could make the with statement usable again, silly examples notwithstanding

Agreed. You even got my vote ;->

Ross Davis at 3/9/2003 12:44:48 PM -
You should ensure that these additional comments get entered into the original comment.  This is the only thing that gets read on the internal system.

Ross

Jon Shemitz at 5/1/2002 8:10:25 PM -
As is, this is pretty much just a convenience / optimization. "with This: Components[Index]" &c.

However, it seems to me that if this were implemented along with a new compiler option that REQUIRES the use of the alias, that the Code Rot issue might go away.

Lasse Karlsen at 5/6/2002 11:48:35 PM -
Current-style workaround:
  a:=MyForm; b:=MyLabel;
  a.Caption := 'Hello world';
  b.Visible := True;

The gain of using new-style with statement:
- no need for a and b variables
- scoping, they would only be accessible within the with statement

Bevan Arps at 6/17/2002 5:50:41 PM -
Seems like a logical extension of the current syntax.

Would also be good if we could get warnings for nested withs

eg: I have no problem writing

with MyObject.SomeIndexedProperty[42] do begin
  Caption := 'fubar';
  Index := Snafu;
end;

but I've found that code with nested with's, like this:

with MyObject.SomeIndexedProperty[42] do begin
  Caption := 'fubar';
  with Items[9] do begin
    Index := Snafu;
    JobNumber := 18;
  end;
end;

really becomes a maintenance nightmare.

--

Andrew Fionik at 7/28/2008 1:07:29 AM -
I think that ANY code which contains "with" statement becomes maintenance nightmare. I think that "with" should be banned. In addition code completion and debugger's tooltip expression evaluation doesn't work inside "with".

Peter Morris at 8/5/2002 2:33:36 AM -
Warnings for multiple WITH statements is good.

with form, label do
  Caption := 'sdfsdfsdfsfdssdf';

Should at least show a warning.

Paul Ericksen at 9/11/2002 11:18:45 AM -
This is a very good point. To have it warn of the ambiguity.

David Knaack at 3/24/2003 2:25:59 PM -
Its not really ambigious, it takes the innermost scope.

Its just easy to screw up :)  If aliasing were available for WITH, it would be a good thing to generate a hint for code that would be valid in multiple scopes and did not specify for which scope it was intended.

Peter Morris at 8/5/2002 4:05:40 AM -
I have often had to pass
  SortList(WhatGoesHere?) to a procedure of somesort and then had to resolve to creating a local variable.

Maybe you should just stick to using a local variable?

Paul Ericksen at 9/11/2002 11:22:07 AM -
I like the idea of aliasing/temp variables. You could also use temp variables with for loops.

for i := 0 to StringList.Count -1

and have i be a temp (limited scope) variable with a scope only inside of the loop. Then if you wanted to access that variable outside of the loop you could just give it local scope.

John Herbster at 9/25/2003 3:02:41 AM -
This idea of having loop variables automatically given hidden declarations was discussed in the forums at some length.  As I recall, the objections were two:  (1) It was not Pascal and (2) there was concern that there would be cases where it would be difficult to determine the type for the variable.

John Herbster at 2/18/2009 4:20:48 AM -
I have never found cases where it would be difficult to automatically determine the type for the loop variable.  --JohnH

Registered User at 5/16/2003 11:56:06 AM -
would an aliased/temp variable have to be declared?  for example, could you simply create an alias 'i' for the for..next loop, thusly:

for i := 0 to StringList.Count - 1

or would you (still) have to declare it as a scoped variable, thusly:

procedure IgnoreCount;
var
  i : Integer;
begin
  for i := 0 to StringList.Count - 1 do ;
end;

Creating variables (temp/alias or otherwise) "on the fly" seems contrary to delphi/pascal standards, but being forced to declare your aliases (and properly type them?) would make using them in with statements much less useful.

Roger Morgan II at 5/19/2004 7:54:48 AM -
14088  2839689

Bjorge Sfther at 9/24/2003 12:10:23 AM -
One tiny improvement would be allowing "." for denoting the "with'ed" object:

TForm1.Button1Click(Sender: TObject);
begin
  With TButton.Create(Self) do begin
    .Parent:=Self;
    .Caption:='Button 2';
  end;
end;

...as this would allways make it possible to write unambigous code (as long as one doesn't use more objects in with clause).

Kevin Berry at 10/19/2005 6:53:42 AM -
I agree.  I'd love to see this in Delphi.  A "." without a prefixed alias could simply be called the "default alias" i.e. it fits in nicely with the aliasing concept when only a single variable is used in the WITH.  One small difference: "." should allow the methods and properties of all with'ed variables to be accessed.

BTW, I already submitted this as a QC report (#3787):
http://qc.borland.com/wc/qcmain.aspx?d=3787

One of the main productivity improvements would be the ability to use code completion.

The "." should be optional though- if developers wanted to use it they could, otherwise they could use the old form of with statement without the "."

Steven Bliss at 11/1/2004 3:59:17 PM -
I aggree, but would also love the alias option as well.  I am Delphi Guru and use with reasonably often, but restrict to short and obvious grouping to avoid possible abiguity, especially by other coders.  I really wish Borland would fix this once and for all, because as it is now with is like getting a sip of water in the dester, when we really want the whole glass.  It is not hard to do in the compiler, and VB at least has the .<var> resultion syntax ... how insulting for Delphi is that!

Tom Kelsey at 10/3/2003 8:47:31 AM -
one of the pronblems this solves is that there is no way to explicitly refer to the 'with'd var inside the block.
a more concise way of doing this is to have another implicit variable (like 'result' and 'self') e.g. 'it' :

with TStringList.Create do
try
  Add('A');
  Add('B');
  CallProcedure(it); // it = the TStringList
finally
  Free; // S could also be ommitted here
end;

Kjell Rilbe at 6/3/2004 11:26:22 PM -
Except that this won't work when you have multiple with:ed expressions:

with TStringList.Create, MyForm.BigPanel.SubPanel.SecondButton do
try
...
finally
...
end;

Which expression would "it" refer to and how would you refer to the other expression?

Phil Hackett at 12/3/2004 1:33:55 AM -
I'm thinking this looks a lot like declaring a variable mid-way through a function, like you can in C++.  May what we need is an extension to the availability of "var", so you could do this:

var
  S:=TStringList.Create;
try
  Add('A');
  Add('B');
  CallAProcedure(S); // here it's really of great use!
finally
  S.Free; // S could also be ommitted here
end;

The point being that the var would normally have to be outside of a begin...end block, but perhaps that could be changed.

Lutz Ristau at 2/11/2005 3:23:22 PM -
I also expect this to be done by Borland, especially when considering the very long time this request is "opened", how many people are interested in that and how big it would enhance the language.
This  and a "step" clause for the "for" statement and a string-driven "case" are easy to implement in the compiler. Why it is not done until now?

Jeremy North at 6/8/2005 5:48:55 PM -
I don't see it as a language enhancement. I see it as breathing life into a statement that should be allowed to die.

Andrea Raimondi at 5/17/2005 11:37:22 PM -
Imho the main problem with With is that it has probelms with bad designed identifiers.

The "Name" property example is a "classic":

1) Why would you want to assing a name at runtime on your own is well beyond me in most cases,
2) Assigning a "Name" identifier to a property means willing to suffer.

Long property names don't make code easier, but unreadable.

Plus, having too many dotObject is a sign of bad design - imho.

Another thing that takes problems - imho - is the use of a construct like:

with TSomeClass.Create(...)...;

Especially wirth forms, this kind of statement should be OUTSIDE of the form, either in another
class or as a separate routine.

Cheers,

Andrew

Kevin Berry at 10/19/2005 7:03:37 AM -
I recommend the following syntax rather (replace : with := because that's really what you're doing even though the type of "a" and "b" is being determined by the compiler- perhaps we need a syntax for that so that it could be generic in use??):

var
  MyForm: TForm;
  MyLabel: TLabel;
begin
  ...
  with a:=MyForm, b:=MyLabel do
  begin
    A.Caption := 'Hello World'; // a simple Caption :=... would be ambiguous to the human reader
    B.Visible := True;  // same here
  end;
  ..
end;

This got me thinking about a new addition to C# 3.0.  With LINQ they're using "var" to automatically create a variable with an inferred type.  Perhaps that would fit in with this change?

Jolyon Smith at 10/20/2005 12:45:50 PM -
My 2 cents:

   with S: TStringList.Create do

is IMHO _nothing_ like

   on E: Exception


In the case of the exception, the exception object exists and the code _needs_ a way to obtain a reference to that runtime generated object.  In the case of the suggested change to with, there is no pre-existing object.  what is being proposed is simply a way to reduce typing.

There may be a case for supporting specific example given - i.e. when using an object whose existence is limited to the lifetime of the "with" block.

IMHO for any/all other examples the compromises and special cases involved are not worth the effort, given that all is being saved is a bit of typing.

Declaring variables in the middle of executing code?  No thank you.  The Exception case is (I think I'm right in saying) a unique example of this in Object Pascal.  Which is fine given that it is an -exception- related matter.  The Exception to the rule...?  Quite fitting.


As I say, for temporary _objects_ (as opposed to just temporary _object_references_) there may be an argument, but in that case I would propose that the language change make the destruction of the temporary object both implicit and compulsory.

i.e.

   with s: TStringList.Create do
   begin
       // work with stringlist
   end ;
   // s out of scope and is destroyed


should be analagous to the current:

   with TStringList.Create do
   try
       // work with stringlist
   finally
      s.Free ;
   end ;


Just my 0.02

Karol Bieniaszewski at 9/22/2006 12:13:23 AM -
I see that this can only do more error in coding.

do this in standard way.

Var S: TStringList;

........
S:= TStringList.Create

with S do
try
  Add('A');
  Add('B');
  CallAProcedure(S);
finally
  S.Free;
end;

Alex Fekken at 2/9/2007 10:48:00 PM -
My vote is for what I think was the original intention: making with statements safer by introducing aliases. To get the real benefit it should be possible (compiler switch) to make the alias mandatory or at least generate a warning if there is none.

Note that this is very different from introducing a temp variable (which is possible anyhow but less safe) in that, I would say by definition, an alias would be read-only and it would also have it's scope restricted to the with statement. I would therefore argue against using the := operator as that would seem to imply creating 'just another reference' to the underlying item (and one that could be changed to something else) rather than aliasing it.

Of course the additional benefit of being able to properly debug the aliased properties, fields and methods would also be very welcome.

m. Th. at 7/18/2007 3:27:42 AM -
Agreed, except with the thing that the alias would be 'read-only'. Imho, it should be a 'normal' variable but with the scope limited to the 'with' block.

Igor Funa at 8/21/2007 2:01:00 PM -
My opinion is that those named "variables" are not a good idea. The only need for this variable is when you need to use this value within the (multiple) With statement. In these cases I propose the following:

Var
  MyForm, CaptionForm: TForm;
  MyLabel, VisibleLabel: TLabel;
begin
  ...
  with MyForm, MyLabel do
  begin
    Caption := 'Hello World';
    CaptionForm := Self [0];
//    CaptionForm := With [0];  { option: avoid Self name }
    Visible := True;  
    VisibleLabel := Self [1];
//    VisibleLabel := With [1]; { option: avoid Self name }
  end;
  ..
end;

Within the With statement there is an array which holds whatever is in the With statement. This could work also in nested With statements - the Self index would just increase in these cases.
Name of this "variable" could be Self - it is always referenced as array to avoid ambiguity with standalone Self within methods.
Another option is to use With [n] array. I prefer this one.

Anyway, is there any benefit in code generation for using with statement?
I assume CSE (or some other optimization) would (could) take over repeated calculation and loading of whatever is in the With statement.
If this value can not stay in register the compiler could spill it to some location and use the value from there.
This would be a "compiler generated" With statement.

Márton Balassa at 9/27/2007 3:20:42 AM -
Imho this is a retarded idea, such as the ". without a prefix" thing. Don't forget that we are talking about Delphi; we're not inventing a brand new language, but posting suggestions to enhance an existing one. The "with something as s do..." syntax would perfectly fit into the object pascal language, and solve the problems of "with" statements:
1) this way, function results, properties, etc. could be treated as a variable inside a "with" block, and not be calculated over and over again
2) we could assign meaningful names to these values
3) it would elliminate the dangers of the original with statement (ambigous names, etc.)

Consider this:

Let's assume that FieldByName is a relatively slow function (bunch of string comparisions, etc). In this code, we can save 5 FieldByName calls:

  with
    Dataset.FieldByName('qtty') as Qtty,
    Dataset.FieldByName('discount') as Discount
  do
  begin
    if Qtty.AsInteger > 50 then
      Discount.AsInteger := 30
    else if Qtty.AsInteger > 30 then
      Discount.AsInteger := 20
    else if Qtty.AsInteger > 10 then
      Discount.AsInteger := 10
    else
      Discount.AsInteger := 0;
  end;

The block-local variable approach would also make sense:

  with I: Integer do
  begin
    // scope of I is inside the block
  end;

This way we could finally get rid of those old, 40-line long var sections...

Steven Bliss at 10/12/2007 6:00:13 PM -
WHEN THE HELL IS CODEGEAR GOING TO FIX THIS ... IT HAS ONLY BEEN A DECADE!!!!!!

BICK Guillaume at 6/28/2008 3:28:04 PM -
(sorry for my English)
This is a good idea to add identifier in with statement, but i disagree the use of ':=' or assimilated. The alias that you manipulate inside a 'with' IS the variable, not a copy.

var Rec : TSomeRec;
...
with R := Rec do // R is a copy ?
begin
  R.Field := 0;  // modify Rec.Field ?
  R := OtherRec; //???
end  

So I prefer the use of '=' (during the scope of the with the alias and the variable are truly equivalent) :

with Fld = MutliArray[i,j].OtherArray[k].MyField do
begin
  Fld.SubField := 0;
  Fld := AValue;
end;
<=>
pFld := @(MutliArray[i,j].OtherArray[k].MyField)
pFld^.SubField := 0;
pFld^ := AValue;

Fld in this example work like a C++ reference.

Henrik R. Carlsen at 2/9/2009 3:41:55 AM -
Why not use same syntax as for the exceptions in a try-except?

with a: frmMain do
    TfrmSubform.Create(a).ShowModal; // action = caFree

or

with a: frmMain, s: frmSubform do
begin
  a.Caption := 'Main form';
  s.Caption := 'Sub form';
end;

That syntax seems perfectly logically to me.

John Herbster at 2/18/2009 7:54:24 AM -

I agree with Kevin Berry (Oct. 2005) and Marc Hoffman’s (Feb. 2009) suggestions that the best syntax for a with-alias statement is:  
  with <alias> := <record, object, or interface> do <statement>
where, as in all of these proposals, the assignment would be by reference and not the variable itself.  And further, the use of the alias name would be required as a qualifier prefix for accessing the individual fields of the aliased.

This syntax would make obvious that the with statement assigns the reference pointer to the alias at the moment of its execution and that the alias is not a declaration of an equivalence.  

This syntax would allow the compiler to pass the alias reference into procedures as a const, var, or out parameter and for the compiler to treat the fields of the alias as appropriately read only or read-write and yet not allow the changing of the reference pointer itself.

This syntax, by requiring the use of the alias name as a qualifier, would remove the potential for a lot of the present mistakes that occur with the use of the present "with" statements.

As far as potential problems with the use of aliases overriding other names, I suggest that compiler warnings would be produced to warn when local variables would be obscured.  

--JohnH, 2009.02.18

References:

Duplicate QualityCentral report submitted by Marc Durdin
in Jan. 2007:
  Report No: 38563            Status: Open
  with statement really should support aliases
  http://qc.codegear.com/wc/qcmain.aspx?d=38563

Tom Kelsey wrote in October 2003:
One of the problems this [QC report #697] solves is that there is no way to explicitly refer to the withed var inside the block.

Kevin Berry wrote in October of 2005:
I recommend the following syntax:
  with a := MyForm, b := MyLabel do ...

BRIK Guillaume wrote in June 2008:
This is a good idea to add identifier in with statement, but I disagree the use of ':=' or [assigned].  The alias that you manipulate inside a 'with' IS the variable, not a copy.

Marc Hoffmann wrote (in e.p.delphi.non-technical) in February 2009:
While with would be a value assignment. if anything it should be:
  with x := SomeLongExpression do ...

Zigmund Bulinsh at 3/10/2012 11:47:49 AM -
Why not use more nice syntax. All these ":" are not good and difficult to implement.
Why not like this:

with Object.Variable as V do
  begin
  end;

Using "AS" syntax like in SQL.

William Egge at 10/23/2014 11:45:58 AM -
"as" is already taken for typecast checking.

Server Response from: ETNACODE01