Introducing “unit namespace”

DWScript now has a new “unit namespace” feature, which aims at killing several birds with one stone:

  • supporting “classic” namespace as found in Java or .Net
  • supporting aggregate namespaces and conditional units
  • supporting unit deprecation and splitting with backward-compatibility
The syntax is as follow:
unit namespace Foo.Bar; [deprecated 'deprecation message';]

uses Foo.Bar.One, Foo.Bar.Two, WhateverUnit;

and that’s all for the Foo.Bar.pas file. Unit namespaces don’t expose or implement anything.
With the above unit namespace, if in regular code you have

uses Foo.Bar, OtherUnit;

it will be equivalent to having written

uses Foo.Bar.One, Foo.Bar.Two, WhateverUnit, OtherUnit;

So all the unit namespace’s “uses” are brought into scope when the unit namespace is used.

Use for classic namespaces

In that case the unit namespace code will have to be auto-generated dynamically by your IDE or compiler environment, which will be responsible for collecting all the individual files that compose the namespace and presenting them in the unit namespace.

Unit namespaces files don’t have to be generated on disk or anywhere: you can just generate in memory in the DWScript event or interface you’re already using to feed source files. It’s just a way to tell the compiler all the source files that are part of a particular namespace.

Use for aggregated namespaces and conditional units

Unit namespaces are oblivious to what they’re made of, so you can aggregate unit from different namespaces.

Typical usage case is when some class defined in FooBar is using or referring other types defined in OtherUnit, and it’s common to need types of OtherUnit when using the class. You can use a unit namespace to bundle those as a convenience – just don’t got overboard and package everything and the kitchen sink!

Also, as unit namespace are source code, they support conditional directives ($ifdef, $include etc.), meaning you can use them for conditional compilation, depending on target.
Remember the Kylix era with Controls vs QControls in “uses” that had to be placed in all cross-toolset forms or components and controled with directives? If unit namespaces had been available, this conditionality could have be segregated rather than spread over every form.

Use for deprecation and refactoring

Suppose you want to rename FooBar to Foo.Bar, in Delphi you could use unit aliases, but that’s a compiler setting, and it won’t encourage or facilitate projects to be migrated. With unit namespaces you can rename FooBar to Foo.Bar and keep FooBar as unit namespace:

unit namespace FooBar deprecated 'Renamed to Foo.Bar with a dot!';

uses Foo.Bar;

and with it the old code will compile, with a deprecation warning, thus facilitating migration.

You can also leverage it for refactoring, f.i. if FooBar isn’t just renamed to Foo.Bar but also split into Foo.Bar and Core.Bar, f.i. if someone had put Core.* stuff into FooBar, and you’re cleaning that up. You can then introduce

unit namespace FooBar deprecated 'Splitted to Foo.Bar and Core.Bar';

uses Foo.Bar, Core.Bar;
This will keep the code compiling, just with a warning.
If you’re publishing libraries and don’t have control over all your library users’ code, this particular use case for “unit namespace” offers a lot of flexibility. Renaming and dispatching classes across units can now be a smooth process, with deprecation warnings that simplify migration of user code.

8 thoughts on “Introducing “unit namespace”

  1. I wish Delphi had that. 🙁

    Especially when trying to not put tons of classes of the same domain into one unit you often end up with dozens of units in the uses clause.

  2. I Agree that the Foo.Bar.* is better than just Foo.Bar.
    The * symbol automatically explain that I want all units in the namespace, instead of a single unit called Foo.Bar.

  3. @Stefan Glienke
    The problem with that is that Delphi is not a multi-pass compiler. And with that namespace feature you run into circular unit references really fast. Or am I wrong?

  4. @Andreas Hausladen
    Yes single pass reduces the “ubiquitousness” of namespaces, but they would still be useful to expose a library to the outside (but inside a library circular references can be problematic indeed). Regardless, it would be useful to minimize the “stuffing” of uses clauses, and make it more practical for EMBT to refactor & rename VCL & RTL units (as illustrated by the dotted namespace transition currently “happening”)

  5. At first glance this appears to be a bit backwards. That is, you have to declare a namespace and then put things in it. Shouldn’t a unit declare the namespace that it is part of, itself ? Apart from anything else, it seems to me that using this “this namespace consists of” approach (vs “I am part of a namespace”) you can end up with two units in two different namespaces, no ?

    unit namespace Foo.Bar;

    uses Foo.Bar.One, Foo.Bar.Two;

    and

    unit namespace Snafu;

    uses Snafu.One, Foo.Bar.One;

    Having said that, whilst this might make me a little uncomfortable, is it actually a problem as such ? If a unit then uses both Foo.Bar and Snafu namespaces, presumably the aggregated unit lists that comprise those namespaces will be reduced to a list of unique units.

    So perhaps not a problem at all.

  6. @Jolyon Smith …and I used “unit namespace” to leave open the possibility of having more traditional namespaces introduced in the future (with just “namespace” as in Oxygene).
    “Unit namespace” had more immediate benefits that traditional namespaces don’t have when it comes to refactoring and renaming, and that’s the immediate need that begat this language extension (got old stuff that needs to be re-arranged, and outright breaking of user code wasn’t an option)

Comments are closed.