Fog Creek Software
Discussion Board




With

Pascal and VB have a 'with' keyword that lets you set a context for variables.  So:

with windows.get(TOP).controls[12].canvas do begin
  moveto(10,20);
  lineto(30,50);
end;

is equiv to:

windows.get(TOP).controls[12].canvas.moveto(10,20);
windows.get(TOP).controls[12].canvas.lineto(30,50);

which is easier on the eyes?  Would c++ and Java be better with a 'with' keyword?

i like i
Thursday, June 12, 2003

No.

Adding the with keyword would invalidate a lot of existing C++ code, it would require a special case of scoping rules, and solves no new problems.

if it bothers you

whatever& with = windows.get(TOP).controls[12].canvas;

with.moveto(...);
with.lineto(...);

Provides almost equal convenience.

Mr Jack
Thursday, June 12, 2003

It's not just visually better....

I always used the construct anyway, for no other reason than it made my code easier to read, but I was told by someone a couple of years ago that there is a performance advantage to using 'with' as the reference to the object is not obtained for every line in the code......or something.

Shows the dangers of taking comments at face value ;)

One of these days I'll get round to checking that.

Justin
Thursday, June 12, 2003

Personally I hated it. It obscures where the contents of the "with" are coming from (if that is clear).


Thursday, June 12, 2003

No, you run the risk of silently importing or hiding identifiers from the outside scope if the structure changes.

Just alias the object with a shorter-named variable and use that. You will either get the same code, or something slightly better.

Tom
Thursday, June 12, 2003

With is the work of the devil, let me explain:

(We use VFP)
>>> Bad Example >>>>
Procedure Proc1()
    With Thisform.someControl
          Proc2()
    EndWith
EndProc

In a PRG far, far away with no comments about what actual WITH statement this relies on:

Procedure Proc2()
    .Parent.someothercontrolontheform.someproperty = somevalue

    && etc, etc. with no other comments
EndProc
<<< End Bad Example <<<

So now you have a seperate procedure that can't be called from anywhere else in the whole program.

Maybe the WITH command needs a limit put on its scope so it can't go between different procedures or something?

Chris
Thursday, June 12, 2003

I hate 'with'.  It makes code less readable to me because it makes method calls look almost identical to global function calls.  Not to mention the problems you incur if you move code around.  I agree wholeheartedly with those saying just assign any long expression to a reference variable and use the reference.  This solves the performance problem your colleague mentioned without incurring the problems of 'with'.

One-Armed Bandit
Thursday, June 12, 2003

With seems to be one of the biggest culprits I've found for writing bad code in VB.

Worst of all are nested withs. Ie :

With Object1
    .Foo
    .Bar
    With Object2
        .Bar
    End With
End With

Totally frustrating to read.

Better Than Being Unemployed...
Thursday, June 12, 2003

Maybe slightly OT, but Ruby has a very nice structure for this sort of thing, using code blocks and a yield keyword.

The sort of place you would most usually come across a block would be in an iterator

e.g.

array=[1,2,3]
array.each { |x| print x," " }

=> 1 2 3

This passes each value used in the array out into the block, using x as a local name for the value passed out. Instead of of { }, you can delimit the block using do ... end.

Then there is a yield statement, used to pass out values from a subroutine to a block. For example

def series
  x=1
  while (x<100)
      x+=x
      yield x if block_given?
  end
end

series  { |y| print y, " " }

=> 2 4 8 16 32 64

Yield doesn't quite do what with does, but it is a very nice way of gaining a local context, and also lets you overwrite generic behaviour with specifics where you need to.

Best regards,

treefrog
Thursday, June 12, 2003

treefrog,

That's not the same thing.  The ruby construct is for iterating through collections.  The example in the original post does no such thing.  It is operating on a single known member of a collection.

Oren Miller
Thursday, June 12, 2003

Don't blame the tool for the actions of those who wield it...

The "nesting a function call within a with" and "nested withs" constructs are simply bad coding.

The reasons for using With:
1) object only resolved once
2) when properly used, code is more readable
3) makes code easier to type

Compare:

cmdRecordset.Parameters.Add(cmdRecordset.CreateParameter(name, type, length, value)
cmdRecordset.Parameters.Add(cmdRecordset.CreateParameter(name, type, length, value)
cmdRecordset.Parameters.Add(cmdRecordset.CreateParameter(name, type, length, value)
cmdRecordset.Parameters.Add(cmdRecordset.CreateParameter(name, type, length, value)

or

with cmdRecordset
  .Parameters.Add(.CreateParameter(name, type, length, value)
  .Parameters.Add(.CreateParameter(name, type, length, value)
  .Parameters.Add(.CreateParameter(name, type, length, value)
  .Parameters.Add(.CreateParameter(name, type, length, value)
end With

I generally only used with when I had a tight grouping of calls to the same object - either something like the above or a series of property assignments.

Philo

Philo
Thursday, June 12, 2003

Oh, don't get me wrong, I'd agree with your example there - just most of the code I've seen has With used in all the wrong places.

Better Than Being Unemployed...
Thursday, June 12, 2003

I introduced "With" to VBA because Excel macros were full of cases where you did ten things to the same cell, range, chart, etc. For example if you have a macro that creates a chart, you need to set about 23 options on the chart before it looks the way you like it. In the user interface, you select the chart and do 23 things. This maps nicely to opening a with block and doing 23 things, so the macro recorder could generate with blocks and thus create vastly cleaner code.  (Try it... the macro recorder generates With blocks all the time).

To the macro recorder, a with block is better than setting a temporary object variable because the macro recorder doesn't know what other variables are already in scope and it can't be trusted to pick a name that doesn't collide.

Another reason for With in VBA is that the language was designed so that you didn't have to understand what a reference or pointer was to write code. Of all the people that write Excel macros, only about 1/10th of them have the aptitude to understand pointers and references. You may look down on them but if you're concerned about the usability of a programming environment, avoiding pointers and references helps more than just about anything else.

(Another way we avoid references in VBA is that whenever you have an object, there is some kind of property of that object, usually an index or a string, which indicates which one it is. So people can do this:

Dim i
i = myRow.Index

then

  ...  myRow(i) ....

Here an i is serving the same purpose as a pointer without actually BEING a pointer, and probably 10 times as many excel-macro-tinkering-people with no programming training can make it work.

Last point. Some people in this discussion seem to be unaware that to use With in VBA, you have to put a . in front of every member that you want to be relative to the With statement referant.

With ActiveWorksheet
  .Save
  .Close
End With

The leading . means "in the scope of the with statement." That solves the scoping problem completely and makes the code more readable and completely deterministic.

Joel Spolsky
Thursday, June 12, 2003

"I introduced 'With' to VBA" - Joel

Seriously?

Stress
Thursday, June 12, 2003

Yep, here's the spec from December 1991:

(critical) Repeatedly specifying the same object (or part thereof) gets tedious. We need a Pascal-like "with" statement that might work as follows:
            with [book1.xlw].[foo]
                [d3].FontName = "Helvetica"
                [d5].Formula = 25
            end with

Actually, a neat keyword for this feature might be "tell":
            tell [sheet1.xls!a1]
                Formula = "=SUM(R1C1:R5C5)"
                FontName = "Helvetica"
                Number = "General"
            end tell

For recording, our strategy would be as follows:

*    when the selection or active window changes, create a new tell context with the current selection
*    collapse tell clauses containing 0 statements to nothing
*    collapse tell clauses containing exactly 1 statement to eliminate the tell clause

Joel Spolsky
Thursday, June 12, 2003

Indeed.  Note that Joel's clever construction of "With" has the nice (though abusable, as noted) property that one can lexically determine the value of the "missing" prefix.

This is not the case with the Javascript/JScript "with" block introduced by Netscape. Let me just take this opportunity to tell you all to never, ever, ever use the "with" block in JScript.  It makes the code slower, it eliminates our ability to do compiler optimizations, and it makes the code harder to read.  There is absolutely no point to using it.

Lemme give you an example.

case 1:

var x;
x = foo.bar;

case 2:

with(foo)
{
  x = bar;
}

In the first case we can at compile time determine that x is a local, and generate more optimal code as a result. 

In the second case, we have to generate code that searches foo dynamically to see if we are assigning to "foo.x" or to the local "x".  Worse, if "foo" is an extensible aggregate object (like, say, the "window" object in IE) we have to search the entire transitive namespace of the aggregate in order to determine that in fact we need to bind to the local x.  (And then when we do bind, we bind by name rather than by ordinal.) 

Worse, what if you _accidentally_ have an "x" property on "foo"?  This is quite possible if foo is an aggregate.  You have just written a bug into your program.

The reason the runtime interpreter has to do all this work is because the code is not clear, and that means that humans reading the code have to do the same thing -- to understand whether this code means x = bar, x = foo.bar, foo.x = bar, or foo.x = foo.bar the reader needs have complete knowledge of every method and property of foo.

I have no idea why Netscape designed a language construct with these semantics, but they did.  Avoid, avoid, avoid.

Eric

Eric Lippert
Thursday, June 12, 2003

Joel,

How come you've got access to a Microsoft internal spec? Do they not mind as it's an old one or do they not know that you have it?! Just curious.

John Topley (www.johntopley.com)
Thursday, June 12, 2003

The With statement in Pascal and VB are semantically equivalent to this:

  Canvas *p = windows.get(TOP).controls[12].canvas;
  p->moveto(10,20);
  p->lineto(30,50);

The only difference is the p-> is implicit in VB/Pascal, and explicit in C++/C#/Java/what have you.

In fact, if you look at the generated assembly code from Pascal or VB, the above is EXACTLY what the compiler is doing under the hood.

Chris Tavares
Thursday, June 12, 2003

Joel's statements are certainly valid, especially in the context they where given (VBA code for Excel automatically generated by the macro recorder). For general purpose OO programming however, I believe that the use of 'With' indicates that the various properties/methods should have been grouped as members of a specific object, and that the code executed within the 'With' should have been a method of that object.

Another way to look at it is that the 'With' statement plays with the scoping rules by transforming a specific object into a scope. This indicates the close relationship between scopes, or more precisely closures, and objects. Indeed, programmers using functional languages like to quip that objects are a poor man's closure.

To check out this scope/object relationship taken to extreme see:

http://w3future.com/html/loell/

Dan Shappir
Thursday, June 12, 2003

Follow along, John, I wrote that spec.

Joel Spolsky
Thursday, June 12, 2003

Joel,

I think John means that he thinks you probably should have handed that back to Bill as you were leaving Redmond.

Chris Ormerod
Thursday, June 12, 2003

Dan, you should see how many properties a cell in Excel has. If making a cell bold and red requires a call to a method with lots of arguments, you're going to have 40 arguments, 38 of which will be "do not change" sentinals.

Joel Spolsky
Thursday, June 12, 2003

Oh right. Sorry Bill. To be fair it has been baked into the product for 11 years, it's hard to see it as secret.

Joel Spolsky
Thursday, June 12, 2003

Lol: I said it, I was wrong, Joel likes it, and suddenly it isn't such a crazy idea after all!  Joel, you're quite a opinion-setter :-)

i like i
Friday, June 13, 2003

Hey! Since when does "well that's what Microsoft does" win ANY argument with programmers?

Philo

Philo
Friday, June 13, 2003

I think Joel's idea for putting With into VBA, and the examples he's given are sound.

However, his intended audience (Non techies using Excel as part of their job who want to knock a macro up quickly) is definitely not the same audience as the developers I've inherited code from, who being professional software developers most definitely should understand pointers and references!

And the examples of good usage are different from the places I've seen it abused eg: in three page long functions enclosed in a With acting on a seemingly random object with various nested Withs inside that.

Right tools for the right job etc...

Better Than Being Unemployed...
Friday, June 13, 2003

As for the VFP example, there is absolutely no point in using 'with' in code that can be addressed outside of the required context but in context within a method of a form its entirely reasonable.

If I remember rightly Algol and PL/1 have the same construct.

Simon Lucy
Friday, June 13, 2003

Joel, I said in my original post that With makes perfect sense in the context you described. What I meant in my post is that when I design an object hierarchy to use in my own applications, whenever I find myself using With (if the app is written in VB) or wanting to use With (if the app is written in C++ or Java) it's usually a clear indication that the sequence of set and get operations should have been encapsulated in a method.

It's also important to realize that this property based programming, that fits so naturally with Excel scripting (and VB GUI apps, and browser DOM manipulation) can have significant adverse side-effects in other contexts. For example, it doesn't work well with multi-threaded apps (because the sequence of operations isn't atomic) and result in a huge performance hit when marshaling is required (e.g. DCOM or SOAP).

BTW, I like VB's implementation of With more than that provided by any other programming language that supports it (that I'm familiar with). The . prefix makes it clear which variable is an object member and which is simply local (automatic). It also works great with InteliSense. I actually like it so much that I wish other OO languages, such as C++ or Java, would have used the . prefix to indicate access to members inside methods. That would have saved me the need to prefix member names with "m_".

Dan Shappir
Friday, June 13, 2003

You don't need this in C and C++ because you have pointers.  The original version of Pascal was much less free and easy with pointers, so "with" made sense.  Note that Wirth took this statement out of Oberon, a later successor to Pascal.

James
Friday, June 13, 2003

Well is having a pointer exactly the same thing?  I do use them for this purpose, to alias a complicated expression, but I think you will pay for an extra indirection if the compiler doesn't optimize it out.

e.g.

foo->[goo[i]].a = 1;
foo->[goo[i]].b = 2;

having "with" basically is syntactic sugar that lets you not type the same expression twice.

if you use the C way, you have to do an extra assignment an indirection per field accessed:

void* p = &foo->goo[i];
p->a = 1;
p->b = 2;

I'm not sure how optimizable this is... I'll try it out someday...

Andy
Friday, June 13, 2003

nevermind the fact that most of the expressions above don't compile, I think you get the point : )

Andy
Friday, June 13, 2003

*  Recent Topics

*  Fog Creek Home