WITH IS EVIL!
WITH IS EVIL!
God damn it, I know it makes code easier to type, but its hard to read and maintain. Get rid of the bloody thing completely!
Of course, I'm even more cross because this is some of my very own code I am complaining about, I really don't know why I used a with, the block was only half a dozen lines and just confused matters.
Whilst upgrading the code to remove the Containers unit (its not supported on NextGen platforms, so I have to make things work with Generics.Collections instead, bye bye D7 support for this code) and refactor a couple stupidities in my original design (they always creep in, don't they) I ended up with two class members of the same name. The with block then looked OK but I was in fact not access the member I thought I was.
I wasted a morning stepping and debugging a stupid error that would have been obvious had I avoided the WITH block. In fact the error would never have happened if I hadn't used it in the first place.
Now why the heck won't my XE2 written unit tests compile in XE5? GRRRRRRRR!
God damn it, I know it makes code easier to type, but its hard to read and maintain. Get rid of the bloody thing completely!
Of course, I'm even more cross because this is some of my very own code I am complaining about, I really don't know why I used a with, the block was only half a dozen lines and just confused matters.
Whilst upgrading the code to remove the Containers unit (its not supported on NextGen platforms, so I have to make things work with Generics.Collections instead, bye bye D7 support for this code) and refactor a couple stupidities in my original design (they always creep in, don't they) I ended up with two class members of the same name. The with block then looked OK but I was in fact not access the member I thought I was.
I wasted a morning stepping and debugging a stupid error that would have been obvious had I avoided the WITH block. In fact the error would never have happened if I hadn't used it in the first place.
Now why the heck won't my XE2 written unit tests compile in XE5? GRRRRRRRR!
Two simple changes would make the with statement more bearable:
ReplyDelete1. Fix the freak'n debugger - with has been a part of the pascal language since it's inception in the early 70s. Absolutely no excuse for the debugger going silent inside a with statement.
2. An ambiguous reference warning - with isn't the only place this would be useful. There are plenty of places where identifiers with the same name but different scopes can get confusing.
Agreed, but perhaps its time to move away from it?
ReplyDeleteActually I like the ambiguous reference idea, that would help with debugging things.
Kenneth Cochran About your second point: unless Delphi will get case sensitivity just please NO! I like to use unprefixed argument names (usually write them lowercase like f.i. count) that might have the same name as a property (Count). This would just produce unnecessary warnings.
ReplyDeleteIf they are going to change something that it's fine grained scoping of variables and introduction of something like using (or revamped with syntax). But that actually looks a bit weird with the way pascal defines variables (type after the identified).
with x: TFoo := TFooBar.Create do
try
x.Baz;
finally
x.Free;
end
or with an interface where it gets niled after the with scope:
with x: IFoo := TFooBar.Create do
x.Baz; // after that x gets set to nil and thus TFooBar gets destroyed
x.Something; // compiler error, variable not defined
if the with is to remain, it would be a huge benefit to do away with support for multiple items in the with: with a, b, c do is beyond dangerous.
ReplyDeletePreach it, brother!
ReplyDeleteWas bitten by with just the other day. but saved by a "method not used" hint. Turned out a that an event handler was named the same in the current scope, as for the with referenced variable, so
ReplyDeleteprocedure TThisClass.Setup;
begin
with SomeObject do
begin
OnEvent := DefaultEvent;
// lots of more setup
end
referenced SomeObject.DefaultEvent instead of TThisClass.DefaultEvent;
leaving TThisClass.DefaultEvent unreferenced.
yup. code smell.
ReplyDeleteStefan Glienke - I'd like something like that, but can't the type of x be implicit? If you need a different type, you do the on the right side?
ReplyDeletewith x := TFooBar.Create as TFoo do
try
x.Baz;
finally
x.Free;
end
as well as
with x := Some.Long[z].Reference[y]
do begin
if not x.IsLevel
then x.RotateTo(0);
x.Rotate(Delta);
end
having sorted my problem and gotten my unit tests to work again (which was not the problem I thought it was) I have once again been pleased at how useful the unit testing has been, mostly passed but a couple of things have cropped up as test failures which I probably wouldn't have spotted otherwise.
ReplyDeleteLars Fosdal That is indeed a good idea - I like it!
ReplyDeleteEven if the ambiguous reference idea were only implemented within the scope of the with statement it would be a tremendous help.
ReplyDeleteSource code is for humans to read. With makes that more difficult. I never use it, and become quite agitated when I have to deal with it in someone else's code.
ReplyDeleteKevin Powick Any fool can write code that a computer can understand. Good programmers write code that humans can understand. --Martin Fowler
ReplyDeleteWhile I hate With, unfortunately some optimizations are only possible using it. Still, I would welcome it's permanent removal. A good start would be a compiler directive to treat it as an error.
ReplyDeleteWhat optimizations are you talking about?
ReplyDeleteAnthony Frazier For example if you have an array of records, and you need to access a couple of fields from one element.
ReplyDeleteUsing a local pointer-to-record variable mostly gets the same performance, but at least in my experience the compiler can be smarter when you use With.
I agree. We have a lot of code here that have WITH statements. And, being new to the system, I keep saying "withs are going to be the death of me". That and pointers..
ReplyDeleteAsbjørn Heid Isn't the local temp variable /exactly/ what the compiler does with the with statement?
ReplyDeleteAnthony Frazier I don't have Delphi installed on this computer so I can't whip up an example, but I'm certain I've seen cases where the compiler did better registry scheduling when not using a local variable or use less temporaries. Something along those lines.
ReplyDeleteA difference is that With has a limited scope that the compiler knows about, while a local variable technically lives for the entire function.
I'll see if I can recall the case. Shouldn't be too hard to find a With in my code, it'll stick out as a sore thumb ;)
On my code, I tend to use With in a safe way, i.e.:
ReplyDeleteprocedure CreateForm( AClass : TMyFormClass );
begin
With AClass.Create( Application ) Do
Begin
ShowModal;
Free;
End;
end;
Other than that, I cringe any time I encounter a With :D
A