Namespaces in most modern languages are, in simple terms, a way to organise identifiers to avoid confusion (between those identifiers). Typically a file will declare (or identify) the namespace for any identifiers it introduces, independently of the filename itself. Conversely, identifiers in a namespace can be brought into scope where needed by referencing the namespace without needing to know which files contain the declarations for those identifiers. Unfortunately the unit module in Pascal/Delphi predates this by some margin and things are not quite so convenient. But there are things we can do to improve matters.
Namespace prefixes are one obvious tool that can be deployed in this area.
These don’t quite provide the level of decoupling of files and namespaces that are found in other languages, and it could be argued that they are really just a kludge.
They allow us to have files which contain alternate forms of otherwise identically named identifiers and to effectively “switch” between which set of files we wish to use. Namespace prefixes are really little more than a slightly more complicated version of unit aliases so let’s do a quick recap of that.
Unit aliases can be useful when we decide to rename a unit and don’t wish to have to go through all of our existing code and change the references to the old name to the new one. We can instead use an alias to let the compiler know that when it sees a reference to the old name treat it as a reference to the new on.
As well as conveniently replacing/renaming units however, we can also use a unit alias to select at compile-time one of any number of different files containing different, equally current implementations of a class (or other declarations, functions, variables, etc), each with the same name and the same public interface (i.e the same
published methods/properties). Code that consumes these classes could compile with any of those possible units, but we must choose just one.
For a simple, contrived example, let’s imagine we have a
TWidgetService class with two different implementations, Flavours A and B. With a unit alias we would implement different
TWidgetService classes in each of two files:
And in a project consuming this service we would declare a unit alias that equates the virtual unit WidgetService to one or other of these, e.g.:
Then in units (or libraries or programs) which use the service we would add the virtual unit to the
program WidgetConsumerProgram; uses WidgetService; var widget: TWidgetService; ..
Since the unit alias is a project level compiler setting, this enables us to ‘choose’ which implementation of our service our project will be built against. In this case the
widget variable will be a reference to an instance of the
TWidgetService class provided by the
WidgetServiceFlavourA unit. To rebuild our project instead with
WidgetServiceFlavourB we simply change the unit alias:
And at a stroke the entire project is now built with the different flavour (B) of our
This is fine for individual units, but what if we have a whole host of classes and other identifiers in a framework, spread across multiple units ? Changing unit aliases for all of the units involved is going to be tedious, not to mention error-prone. This is where namespace prefixes come in and effectively provide “bulk unit aliasing”.
Namespace prefixes work by first assuming that you distinguish between your different flavours of identifiers in a conventional way, by which I mean: following a particular convention. The convention in question being the use of a consistent unit name prefix for each flavour, separated from the “real” unit name by a dot.
i.e. instead of
With the introduction of namespace prefixes, the rules for unit resolution were extended to first look for a unit name that matches the un-prefixed name in a
uses clause with any filename that corresponds to that name plus a namespace prefix specified in the project compilation options.
So just as with a unit alias, in your consuming code you reference the virtual unit name:
WidgetService. The difference here of course is that you may have many, many units for each ‘flavour’.
The resolved unit name and the physical filename must still match. The namespace prefix allows us to omit the first part of the unit filename when we reference it in a
uses clause, as long as a suitable prefix is supplied in the compiler settings to resolve the partial unit name to a full, physical unit name somewhere on the search path. Then if there is a ‘mirror’ universe of other units that exist with some other prefix we can switch to those by simply changing the prefix in our compiler settings.
Just like unit aliases.
The most obvious example of this is of course the various units that comprise the Vcl and Fmx (FireMonkey) frameworks. Unit aliases could have been used to manage cross-framework compatibility but it doesn’t take much imagination to realise how painful this would have been with the large number of unit involved in those frameworks.
If you look for documentation on namespaces in Delphi you will likely come across information that suggests far more is going on than this pretty blunt unit-aliasing tool, particularly this section of the on-line help. As far as I can tell, this is simply hopelessly out-of-date documentation for the ill-fated Delphi.NET compiler. The biggest clue to this is a reference to
dcuil in the section on Declaring Namespaces. This is odd as the copyright date on the page indicates it being written in 2009, when Delphi.NET had been axed and replaced by Oxygene (branded as Delphi Prism).
Oxygene has a completely reworked and explicit namespace syntax and wholly different (and far more straightforward!) namespace rules than those described.
All of this is very interesting but is really just background for what I really want to talk about which is a pattern I have established for simplifying the consumption of multi-unit frameworks and libraries, such as Smoketest. I call this pattern (rightly or wrongly) Scope Elevation and I’ll explain exactly what it entails next time, using some of the new features in the upcoming Smoketest 2.1.x to illustrate how I find it useful.