I was looking at a few packages recently, and noticed a bothering trend: Single-module libraries ending up at the top level.
Why is it bothering ? Well, here's a little puzzle: Does the following code compile?
import std.stdio;
void main ()
{
writeln(foo.stringof);
}
The answer is "maybe". In most case, it will not. However, if the file this resides in is named foo.d
, it will compile. Because the module name accounts for an identifier that can be addressed in the scope of the module (and other modules that import it).
Now imagine what would happen if core.atomic
or core.time
were top-level modules ? Conflicts, conflicts everywhere. It only gets worse with imports, as import atomic;
would insert the atomic
name into the current scope, which would then shadow any symbol named atomic
in any import (there used to be an evil hack in the compiler to make this special case work - it is gone since a few releases).
So what's the solution ? Well we could go with the Java-esque com.foo
, but that'd be a bit ridiculous. Instead, for a few years now, I have started a habit of using the "vanity package", a top-level package with my username, for simple libraries I publish:
- https://github.com/Geod24/bitblob/blob/v2.2.0/source/geod24/bitblob.d#L16
- https://github.com/Geod24/localrest/blob/ddfa8cd3bc2f48e1dfc5edd803b990e5aaa69044/source/geod24/LocalRest.d#L102
- https://github.com/Geod24/minivariant/blob/d20b240d45289a857b4c0d6e7f842a92b3d4a19e/source/geod24/variant.d#L30
It "just works", is pretty simple to remember, easily extendable, and you can be pretty sure no user of your library will run into conflicts. I encourage anyone considering publishing a package to do the same.