The ever evolving DWScript project continues to advance the Pascal language at an impressive pace. Just today it was announced that this scripting version of Pascal now has “namespace” support.
When I first read the details of the implementation, my initial reaction was that it “felt a bit backwards”. The essence of the implementation is that it involves a new syntax for a unit, which instead of representing an actual unit, creates a “virtual” unit that effectively acts to collate or aggregate a number of other units into a single name:
unit namespace Foo.Bar; uses Foo.Bar.One, Foo.Bar.Two;
Then in some other unit you can use both Foo.Bar.One and Foo.Bar.Two by simply using the namespace that is thus declared as using it:
unit MyUnit; uses Foo.Bar; // uses Foo.Bar.One and Foo.Bar.Two
As I say, whilst useful and powerful, it feels a bit backwards to me.
That is, you have to declare a namespace and then say what is in it, when it seems more natural to me for a unit to say which namespace it is a 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 ?
e.g. given:
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.
In fact, it could be seen as a feature, since it would – presumably – allow you to have a namespace that brings an entire other namespace with it. i.e. Snafu might itself bring in the entire Foo.Bar namespace, so that if your unit then uses Snafu, it is automatically using not just Snafu itself but also the entire Foo.Bar namespace, without having to declare this separately.
Convenient, certainly, but what is an irritant when creating code (having to declare used units) can often be a boon when it comes to maintenance (being able to see at a glance what units are used by any given unit).
Caveat Scriptor
I should add at this point that this post is not intended to critique or suggest an alternative approach for DWScript itself. Rather it is inspired by thinking about how such a feature would work in an actual, compiled Pascal project – specifically Delphi.
Looking Forward…
So, DWScript has it’s new namespace declaration unit, but I think even that can be made a bit more Delphi-like if it were to be adopted by Delphi itself.
Delphi already has a module type which acts as – and thus has an appropriate syntax for – a container for other units. The package.
We need to change the module keyword, but that’s OK and indeed desirable, since we are now defining an entirely new module type, giving us a great deal more flexibility in devising an appropriate syntax for that module. But we needn’t work too hard, since a lot of the existing package syntax “just fits”:
namespace Foo.Bar; contains Foo.Bar.One in '..\Foo\Foo.Bar.One.Pas', Foo.Bar.Two in '..\Foo\Foo.Bar.Two.Pas'; end.
This syntax allows us to better reflect the “containment” relationship that a namespace has with it’s members, as opposed to the “uses” declaration. But this still has us declaring a namespace and then identifying it’s contents. It still “feels backwards”.
It makes more sense – to me anyway – for a unit to declare it’s namespace and, continuing the theme of recycling existing syntax where it fits, there is just such a syntax that works in this case. I deliberately invoked that syntax in my “namespace” module example above:
The in clause.
We could simply [ 🙂 ] add support for that to the module declaration in a unit:
unit Foo.Bar.One in Foo.Bar;
This seems very neat and elegant to me but it has one major problem. As Andreas Hausladen mentioned in the comments on the DWScript post: the one-pass compiler in Delphi.
When the compiler finds a unit that “uses Foo.Bar” it doesn’t know that Foo.Bar is a namespace or which units are contained within that namespace. It won’t find out that Foo.Bar.One even exists or that it is part of Foo.Bar unless and until it is asked to reference Foo.Bar.One itself, which sort of defeats the purpose of namespaces, right out of the gate.
If this could be solved as elegantly as the syntax for declaring the membership of a namespace itself (using this in syntax) then I think we’d have a winner. 🙂
One way perhaps of resolving the, uh, resolving problem might be to introduce an initial first-pass in the compilation of a Delphi project, specifically to resolve namespaces. This pass would use the existing uses declarations in the dpr combined with the project (and environment) search path (i.e. the current “unit search” algorithm) to assemble a directory of units and their corresponding namespace ‘containers’, where applicable.
The bulk of this work – the business of locating a given unit on the search path – is already done by the compiler. But with a directory of unit locations produced by the first pass, the compiler would be relieved of this effort. Consequently the “cost” of the first pass would be offset – at least in part – by the removal of this effort from the compiler itself.
It is also worth mentioning that even the “namespace module” would introduce some problems to be solved in any Delphi compiler implementation, since any namespace implementation creates the possibility, and increases the likelihood, of duplicate references to units in a single uses clause:
uses Foo.Bar, // uses Foo.Bar.One and Foo.Bar.Two Snafu; // uses Snafu.One and Foo.Bar.One
Currently, if a unit is referenced more than once in a uses clause (or indeed is referenced in both interface and implementation uses clauses) then a compilation error results. Either this would have to be relaxed to a warning or ignored entirely, if namespaces are to be implemented.
This is perhaps less of a potential problem when a unit is confined to a single namespace, as would be the case if namespace membership were a declared aspect of a unit itself. But still a potential problem.
The Undiscovered Country
Luckily, whilst we might speculate and invent a future namespace feature for Delphi, it doesn’t actually exist as yet (as far as I know). This means we get to decide how we might like that future to actually look, from among the various options.
So which would you prefer:
- A namespace module, declaring the units contained in the namespace
- An in declaration on a unit, with a name resolution pass added to the compiler
- Some other approach ?
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 (libraries that needed to be re-arranged, involving both renaming of units and splitting large units in smaller units, an outright breaking of user code wasn’t an option).
Also, keep in mind the unit namespace code doesn’t have to be written by a human, it can be auto-generated on the fly from the “files” in the search paths. DWS compiler is sandboxed, meaning it doesn’t know about files and paths, so that’s a way for the host application to list all the files that are in a namespace (files which may not be on disk btw, they could reside in a database).
Hi Eric, yes I appreciate you had very specific needs in mind in the DWScript – please don’t think I was criticising that approach, I was simply considering the implications if the same idea were to be brought across to Delphi itself. I did try to make that clear in the post.
w.r.t the refactoring problem, this is a tough one.
On the one hand I can see how it is useful, or at least convenient, but on the other hand, making it easier to not change something that you actually do want to be changed seems to only be storing up trouble for the future. By using a namespace a placeholder or alias for some other named unit(s), you conceivably create a situation where two different people are referencing the same thing by a different (apparent) name, which at the very least is going to make conversations between them potentially confusing:
“I found a bug in Foo.Bar”
(Someone looks in Foo.Bar and finds that it is a namespace unit)
“Um, which unit in Foo.Bar ?”
“What do you mean, ‘Which unit’ ? I just told you: The Foo.Bar unit.”
But as I say, this is perhaps more likely a problem in a more generalised Delphi implementation, rather than in the DWScript space. 🙂
The same issue of ambiguity already exists in Java/.Net namespaces, where people can either report the namespace or the particular file/class within it. I don’t think it has ever been much of a problem: if you get an exception or something, you get the file in the exception or stack, not just the name-space.
And IME people rarely report that unit X doesn’t work, but more than class X or function X doesn’t work.
> making it easier to not change something that you actually do want to be changed
Well, that’s what the deprecated is for, it allows to quickly diagnose (and fix) all that needs be changed without requiring that everything be changed before you can diagnose.
Case in point the current dotted unit renaming in Delphi is a major PITA for cross-version code. When that code is large enough, porting to a new version becomes a major issue.
Delphi only has unit name aliases, but those don’t provide no warnings.
When you have warnings, it’s simpler to split the workload amongst multiple persons, as the code will still compile, one can work on porting module FooBar while another can work on Snafu, etc.
GNAT compiler has something like what you’re suggesting: GNAT project files (.gpr). It serves the aim of locating the sources belonging to different projects in addition to providing project-specific options.
I. e. Ada developers like to name files using “lower-case-minus.ads” way while I prefer “Mixed.Case.Dot.ads” naming. Somebody put sources inside “src” without recursion, another one can put sources inside deeply nested directory structures. Every project can have different layout. Project can specify Source_Dirs and/or Source_Files. Project can specify precise location of particular unit (called packages in Ada).
Every project references projects it depends on and that’s the only thing using so called search paths. Having located every project file compiler can enumerate it’s units despite the differences in file naming and layout. No need to use unit search paths, include search path, object search paths. Just project search paths. Project files grant a dimension of freedom and developers are free to use .gpr for their own purposes. Not just compiler. GPS (Ada IDE) also uses .gpr
1. I’d prefer to not introduce a new file type, but use the dotted unit names as a natural namespace reference. I.e. Vcl.StdCtrls.pas and Vcl.StdActnMenus.pas units both belong to the Vcl namespace. Then when we write in our code:
uses Vcl.*;
we can use here any class from the Vcl namespace.
Moreover we can introduce the sub-namespaces using several dots in a unit name. For ex. Vcl.Touch.Gestures.pas and Vcl.Touch.Keyboard.pas units both belong to the Vcl namespace and the Vcl.Touch subnamespace. And we can write in our code:
uses Vcl.*;
or just
uses Vcl.Touch.*;
if required.
2. To exclude an additional pass of the compiler we must forbid a circular references of the namespaces. I.e. if we have Vcl and System namespaces, and units from the Vcl namespace use the System namespace, then System units can’t use Vcl namespace.
Another problem is that it requires total control in creating namespaces. One can’t add classes to a namespace later (since then the namespace unit has already been made).
Afaik in Java you can add a class by putting a relevant class file in the right place in a new classpath.
Classes can also be part of multiple namespaces. (though I don’t see a direct problem with that)
I’m no fan of namespaces the classpath way. But nearly all proposals to add them to Delphi seem fabricated to me, and even worse.
Baroque, little practical use. (a slightly less chance on short identifier clashes at best, if properly and universally used). Ugh.