Tuesday, August 30, 2011

The "With" Statement

Regardless of which language you use the controversial with statement's intended purpose is to reduce repetitive typing. Some developers think its the greatest thing since sliced bread. Others are certain it was created by Satan. Let's examine a few languages that support it.

Pascal

The with keyword was present in the original Pascal language, developed in 1968-70 and published in Acta Informatica, Vol. 1 in 1971. I've found no description of a with statement in Wirth's prior language projects or in any of the languages which influenced Pascal's design. Though admittedly, my research into its origins is limited. I'll assume for the moment that it was an original idea.
The with statement was originally intended to only be used with record variables. Here's a modification of a sample record from the original language description.

type
  Date = record
    day: 1..31;
    month: 1..12;
    year: 0..9999;
end;

var
 today: Date;
The two following blocks are equivalent:
begin
  with today do
  begin
    day := 25;
    month := 12;
    year := 2011;
  end;
end;

begin
  today.day := 25;
  today.month := 12;
  today.year := 2011;
end;

Object oriented descendants of Pascal added support for classes and interfaces. The main criticism of with is that it introduces the possibility of ambiguous references:
var  day: integer;  
  today: Date;

begin
  with today do
  begin
    day := 25;
    month := 12;
    year := 2011;
  end;
end;


Which day is being assigned to? Intuition may lead you to think that it is the one within the scope of the date record and you'd be right, at least in this case. However, when this block is in the body of an object method, which references multiple other objects in the same with statement it isn't so clear.

Where things really fall apart is when you are referencing a local variable from within the body of a with statement and a field or property with the same name is added to the definition of the object or record referenced by the with statement. Suddenly code that was working flawlessly fails and all you did was add a field to a class definition. Heck, you haven't even referenced the new field in your code yet... or so you think.

So that's Pascal's implementation. I would recommend you avoid using it whenever possible and use great care if you find it necessary to use it. What other languages support the with statement? Let's take a look.

VisualBasic


With today
  .day = 25
  .month = 12
  .year = 2011
End With

Here we see dot notation preceding the fields. This makes it apparent which identifiers are fields and which are not. This syntax was carried forward into VB.NET as well. There is still the possibility of ambiguity if you write a nested with statement. According to the documentation the compiler would only see the fields of the object referenced by the inner with statement until the statement's end. A novice developer could easily overlook this.

JavaScript

JavaScript's with statement functions more or less the same as Pascal's and JS developer's opinions of it are just as polarized as Pascal developer's. ECMAScript 5th Edition "Strict" mode forbids its use and many implementations now emit a warning that it has been deprecated and/or suggest an alternative.

D

Digital Mars' D language supports a with statement with behavior similar to Pascal's with one notable exception: local identifiers and identifiers within the scope of the with statement cannot have the same name. This results in a compilation error, effectively preventing the ambiguity caused by the introduction of new object members.


struct Date
{
    int day;
    int month;
    int year;
}

void main()
{
    int day;
    Date today;
    with (today)
    {
        day++;  // error, shadows the local day declaration
    }
}

Languages with similar features

These languages have similar features but the purpose and/or syntax is too different for them to be consider the same as with.
  • Smalltalk cascades - multiple messages can be sent to the same object separated by a semicolon
  • C#
    • using statement - encapsulates the more verbose try..except..finally syntax when working with implementors of IDisposable
    • object and collection initializers - a more concise syntax for setting object properties at creation time
  • Python with statement - similar to C#'s using statement, it encapsulates the try...except...finally pattern into a less verbose syntax.

Fluent Interfaces are another programming style that can be used to accomplish the same goal as with statements. They use varying combinations of method chaining, nested functions and object scoping along with careful method naming to produce concise, readable statements. The Date record could be rewritten as:
type
  Date = class
  private
    FDay: 1..31;
    FMonth: 1..12;
    FYear: 0..9999;
  public
    function Day(Day: integer): Date;
    function Month(Month: integer): Date;
    function Year(Year: integer): Date;
  end;

function Date.Day(Day: integer): Date;
begin
  self.FDay := Day;
  Result := Self;
end;

function Date.Month(Month: integer): Date;
begin
  self.FMonth := Month;
  Result := Self;
end;

function Date.Year(Year: integer): Date;
begin
  self.FYear := Year;
  Result := Self;
end;

Then in place of the with statement you could write:
begin
  today := Date.Create
               .Day(25)
               .Month(12)
               .Year(2012);
end.

1 comment:

  1. When I started coding in Pascal (probably somewhere 1987) I also thought using "with" was as easy as sliced bread.

    But once you start copying / pasting code blocks and get some code fragments out of their context, the "with" statement is indeed a devil! I have been burnt many times by it, so I put a "personal ban" on that statement.

    However, there are tons of KB in my repository and every now and then I still need to kill one :) Comes together with the "hanging begins"..

    Kind regards, Nils

    ReplyDelete