Darian Miller 366 Posted August 25 New blog post on WITH statements. The topic was recently discussed once again in the Delphi Developers Telegram group which triggered a blog post: https://ideasawakened.com/post/why-you-should-not-use-WITH-in-your-Delphi-code 6 3 Share this post Link to post
corneliusdavid 220 Posted August 25 I agree with this in about 95% of the cases. In fact, I've inherited an old Delphi 7 project that I'm maintaining and upgrading for a customer and it is filled with WITH statements--even nested ones! It's a mess to untangle! A couple of years ago, during the "Delphi Debates" webinar series, I blogged my stance on this: Delphi Debates: With, Goto, & Label--and Exit. I've added a +1 comment to your QP ticket. Share this post Link to post
Brian Evans 109 Posted August 26 If they added the ability to provide an alias the ambiguity would be gone. Aliases in SQL are useful and perform a similar function. Shortening repeated references can make code easier to read, the problem is the current WITH creates ambiguity. A code snipped from the blog post redone with the ability to provide an alias as an example: procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin with dmStoreInventoryData as A do begin with A.tblUpdateItemForStore as B do begin B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; end; end; 4 Share this post Link to post
Darian Miller 366 Posted August 26 2 hours ago, corneliusdavid said: A couple of years ago, during the "Delphi Debates" webinar series, I blogged my stance on this: Delphi Debates: With, Goto, & Label--and Exit. I quoted your blog article in mine. Thanks for the +1. Share this post Link to post
Darian Miller 366 Posted August 26 10 minutes ago, Brian Evans said: If they added the ability to provide an alias the ambiguity would be gone My guess is that Aliasing would bring in a new set of problems but if it's just a preprocessor type replacement, it might be OK. But look at your example code - what is the scope of "NewQty"? Does it belong to A, to B, or to the Form, or to a variable or method somewhere? If it doesn't belong to A or B, what if "NewQty" is added to A or B as a property or method later? Boom, your with-bomb will explode into a nice bug. 1 Share this post Link to post
corneliusdavid 220 Posted August 26 21 minutes ago, Darian Miller said: I quoted your blog article in mine. Ah! So you did! I hadn't read down through all the links. That's a pretty comprehensive study of everyone's opinion! Good job! Share this post Link to post
Brian Evans 109 Posted August 26 2 hours ago, Darian Miller said: My guess is that Aliasing would bring in a new set of problems but if it's just a preprocessor type replacement, it might be OK. But look at your example code - what is the scope of "NewQty"? Does it belong to A, to B, or to the Form, or to a variable or method somewhere? If it doesn't belong to A or B, what if "NewQty" is added to A or B as a property or method later? Boom, your with-bomb will explode into a nice bug. In this example NewQty is a parameter of the procedure. Share this post Link to post
Lajos Juhász 295 Posted August 26 6 hours ago, Brian Evans said: If they added the ability to provide an alias the ambiguity would be gone. Aliases in SQL are useful and perform a similar function. Shortening repeated references can make code easier to read, the problem is the current WITH creates ambiguity. A code snipped from the blog post redone with the ability to provide an alias as an example: There is no need for with in your example: procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); var a: <the required type>; b: <the required type>; begin a:= dmStoreInventoryData; b:= A.tblUpdateItemForStore; B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; With will not make this code any cleaner. 3 Share this post Link to post
Pat Foley 52 Posted August 26 How about qualified with. procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); procedure &With(A: TDataModule; B: TTable; const NewQty: Integer); begin B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; begin &With(dmStoreInventoryData, dmStoreInventoryData.tblUpdateItemForStore, NewQty); end; Share this post Link to post
Brian Evans 109 Posted August 26 Introducing new variables isn't the same as WITH which just adjusts how the compiler handles scoping for which reference a name refers to at compile time. If introducing a new variable isn't an issue may as well use inline variables with type inference for modern Delphi at least. procedure TMyForm.UpdateInventoryItem(const NewQty: Integer); begin var A := dmStoreInventoryData; var B := A.tblUpdateItemForStore; B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; 1 Share this post Link to post
corneliusdavid 220 Posted August 26 1 hour ago, Brian Evans said: If introducing a new variable isn't an issue may as well use inline variables with type inference for modern Delphi at least. This is good in that it eliminates scope confusion and is actually 2 lines shorter than using the nested with because there's no need for "end" statements. And, you could combine the first two var lines into one. Now, if refactoring and the debugger would just work well enough with inline vars that this won't cause frustration down the road if you ever need to change or debug it, then this is the perfect answer! Share this post Link to post
Brandon Staggs 285 Posted August 26 LLMs do write pretty good code poetry. Share this post Link to post
JonRobertson 72 Posted August 27 (edited) I've been working in Delphi 7 projects for the past year. Some are being migrated to 11.3+ and some are staying with Delphi 7. I always refactor with statements away by hand, but even that can be tricky. For example, I learned after refactoring several "with dataset do ... while not Eof do ; Next;" constructs, and overlooking one call to Next, that TForm has a public Next method, related to MDI children of a MDI parent. https://docwiki.embarcadero.com/Libraries/Alexandria/en/Vcl.Forms.TForm.Next Hello infinite loop! Now, go away. Edited August 27 by JonRobertson 1 Share this post Link to post
Tommi Prami 131 Posted August 30 On 8/27/2024 at 5:02 PM, JonRobertson said: I've been working in Delphi 7 projects for the past year. Some are being migrated to 11.3+ and some are staying with Delphi 7. I always refactor with statements away by hand, but even that can be tricky. For example, I learned after refactoring several "with dataset do ... while not Eof do ; Next;" constructs, and overlooking one call to Next, that TForm has a public Next method, related to MDI children of a MDI parent. https://docwiki.embarcadero.com/Libraries/Alexandria/en/Vcl.Forms.TForm.Next Hello infinite loop! Now, go away. This is why long long ago made feature request of compiler/AST assisted with-removed refactoring tool. It never happened. Made new one in the new Bug-tracker: https://embt.atlassian.net/servicedesk/customer/portal/1/RSS-1666 Please comment the bug report and give some pressure... -Tee- 1 Share this post Link to post
Frickler 11 Posted September 4 Do you really need A? As you can have for var i:=1 to 10 do ... why not with var B := dmStoreInventoryData.tblUpdateItemForStore do begin B.AppendRecord([1,'foo','bar',3.1415]); end; and "B" would only be visible inside the "with" block, not outside of it. Currently you have to "fake" it by begin var B := dmStoreInventoryData.tblUpdateItemForStore B.Edit; B.FieldByName('Qty').AsInteger := NewQty; B.Post; end; Share this post Link to post
Uwe Raabe 2064 Posted September 4 33 minutes ago, Frickler said: Currently you have to "fake" it by Which is actually six chars less than the with version. I can see no benefit in such with-V2 syntax. Local begin-end blocks without with, for or while have been part of the language since the beginning. With the advent of inline variables they may get some more use cases now. I would not call that fake in the first place. Share this post Link to post
Brandon Staggs 285 Posted September 4 (edited) 3 hours ago, Frickler said: why not with var B := dmStoreInventoryData.tblUpdateItemForStore do begin B.AppendRecord([1,'foo','bar',3.1415]); end; and "B" would only be visible inside the "with" block, not outside of it. What benefit do you perceive from this? In what way is this superior to less verbose begin..end block? Edited September 4 by Brandon Staggs Share this post Link to post
JonRobertson 72 Posted September 4 2 hours ago, Uwe Raabe said: Which is actually six chars less than the with version. Eight chars, if you include spaces. 2 hours ago, Uwe Raabe said: I would not call that fake in the first place. Agreed. Share this post Link to post
Frickler 11 Posted September 12 On 9/4/2024 at 8:29 PM, Brandon Staggs said: What benefit do you perceive from this? In what way is this superior to less verbose begin..end block? Syntactic sugar. It does nothing more than that begin-end, but makes it clear (to me) that "B" is defined only for the purpose of abbreviating that expression. 1 Share this post Link to post