So far we have discussed identifiers and only briefly touched on what they actually represent. We know that identifiers are used for constants, variables, program names, unit names, type names, etc. But what do they mean and what happens when two different things are given the same identifier? The identifier for the program name is essentially meaningless. It's origin is from the early days of Pascal (the 70's) and it has been maintained largely for historical reasons. Some compilers do not even complain if the line "program ProgramName;" is omitted all together. Unit identifiers are obviously important because they tell the compiler where to look for groups of identifiers (variables, constants, subroutines). The process of matching an identifier with its actual meaning in the code is known as resolving an identifier. Thus, we could have a variable called SomeVar in a unit called Unit1:
unit Unit1;
interface

var
  SomeVar : Integer = 10;

implementation

end.
We could have a different variable called SomeVar in a unit called Unit2. Since these identifiers are in separate units their names do not conflict. Note that in each unit they have a different type and different values.
unit Unit2;
interface

var
  SomeVar : String = 'This is a string';

implementation

end.
If we declare Unit1 in the uses clause of the program, then the compiler resolves SomeVar as the variable in Unit1. If we declare Unit2 then the compiler resolves it from Unit2. But what happens if we declare Unit1 and Unit2 in the uses clause simultaneously? The compiler solves this potential conflict by simply taking SomeVar from whichever unit is declared first in the uses clause. If we want to use SomeVar from the unit declared second, Object Pascal provides a syntax to support this need that is similar to the dot "." syntax used by records:
program Example;
uses Unit2, Unit1;
begin
  Writeln(SomeVar); // Output: This is a string
  Writeln(Unit1.SomeVar); // Output: 10
  Writeln(Unit2.SomeVar); // Output: This is a string
end.
This is what happens if we reverse the order of the units in the uses clause:
program Workspace;
uses Unit1, Unit2;
begin
  Writeln(SomeVar); // Output: 10
  Writeln(Unit1.SomeVar); // Output: 10
  Writeln(Unit2.SomeVar); // Output: This is a string
end.
The portion of code where an identifier can be properly resolved is known as its scope. We have mentioned global variables in a program and local variables in subroutines. Global variables are said to have global scope and local variables have local scope. Variables with global scope can be resolved from any code written after they are declared. Consider the following example:
program Workspace;

procedure ExampleProc1;
begin
  X := 8; // This throws a compiler error because the identifier is undeclared
  // To fix this problem, move this procedure below the declaration of X
end;

var
  X : Integer; // X is global from here on out

procedure ExampleProc2;
begin
  X := 5; // This is valid. 5 is assigned to the global variable X
end;

procedure ExampleProc3;
var
  X : Integer;
begin
  X := 10; // The global X is hidden by the local X here.
           //   This assignment has no effect on the value
           //   in the global X.
end;

begin
  ExampleProc1;
  ExampleProc2;
  ExampleProc3;
  Writeln(X); // What's the value of X here?
  Readln;
end.
Where it is located right now, ExampleProc1 is invalid because it references the global variable X before it has been declared. ExampleProc2 is valid and it assigns a value of 5 to the global variable X which persists after this subroutine terminates. ExampleProc3 is also valid, but X has been declared as a local variable here. This means that the global X is invisible to the code in ExampleProc3's begin-end block. Although a value of 10 is assigned to the local X, this variable disappears when the procedure exits (because it is local). This means that the program ultimately writes 5 to the screen because the global X is referenced in the main program block.
Excercise 3-16.
Solve the following problems.
  1. Without running the program. Determine what outputs are written to the screen.
  2. Without running the program. Determine what outputs are written to the screen.
As we have seen, global variables also exist in units. If a unit global variable is declared in the interface section, it is visible to all code below its declaration within that unit and to all code that uses that unit. If a unit global variable is declared in the implementation section, it is only visible to the code below its declaration within that unit. This difference in visibility between the interface and implementation sections allows us to carefully design the interface and prevent access to more internal aspects of a unit that are not meant to be used directly by other code. This is analogous to driving a car in which the interface to the wheels is through a power steering wheel. The driver does not get direct access to the components that actually turn the wheels (and for good reason). This is again an example of encapsulation.

Finally, we should point out that scope does not just apply at a global and local level. In Object Pascal, scope can be changed for just a few lines of code by using the with statement:
program Example;

type
  TRec = class
    Int : Integer;
    Str : String;
  end;

var
  Rec : TRec;

begin
  Rec.Int := 10;
  Rec.Str := 'test';

  // This code performs the identical function as the code above
  with Rec do
  begin
    Int := 10;
    Str := 'test';
  end;
end.
The with statement here causes the compiler to temporarily add the fields of TRec to the local scope. This allows Int and Str to be resolved as if they were regular variables. Although this example is trivial, the with statement can be extremely useful in the case of records with a large number of fields or when such fields are used in complex expressions.