I have been trying to explain my Namespace implementation in various ways but it doesn't really feel like people grok it. I think it is because people are expecting complexity and there is none to find. So I am giving it another shot. :) This article is long - but it is not because this is complex - it is because I am trying hard to explain.
NOTE: Before reading further - no, no, no, no, no, no, no, no - I do not want to include any solution for namespaces into Squeak at this time (3.9). But I do want people to understand my implementation. :)
Now, a question for you: Do you use prefixes for your class names in order to make sure they don't clash with other people's names?
I do, and lots of other people do too. The SqueakMap classes all begin with 'SM'. Avi uses it - all Monticello classes begin with 'MC' and all Seaside classes begin with 'WA'. So a lot of us use this convention to avoid name collisions.
Is there a problem with this approach? Well, there are some:
- You can't really tell from looking at a class name if it has a prefix at all. Consider HTTPSocket. Is 'HTTP' a prefix for all classes dealing with HTTP? No it isn't.
- You can't really tell where the prefix ends. Or at least, Squeak can't tell. It could make some educated guess based on upper/lower case, but... nah.
An idea then: What if we put in a separator? Like for example '::'. That particular separator I find quite readable, and if I am not mistaken Perl uses it too. And frankly, what separator we choose is pretty unimportant. :)
Try creating such a class right now - say "Prefix::Dummy". Ah, it worked? Yes. Now, try to reference it in a do-it. Oopsidaisy, doesn't work. The fact that you can create that class can actually be considered a bug - either we should be allowed to use $: in class names or we shouldn't.
So global names can't have colons in them - not in the current Parser/Scanner/Encoder combo. Darn. But... can't we fix that? Oh yes, we can - it is Smalltalk after all - it is actually not hard to do, just a few modified and added methods in those three classes and its done.
Goodie, so now you can stuff $: into class names and if we all agree to the new convention of separating the prefix from the rest of the name using '::' then all is done.
Huh? No, I said it's done. That's it. Stop reading. :) Seriously - this is all there is to it, this is my proposal that people have gotten so worked up over.
So the modifications boiled down to allowing global names with '::' in them. Scanner was modified a teeny bit and some small modifications were made to Parser and Encoder.
Now, we can use prefixes (like many of us do today, SM, MC etc) with '::' inserted to separate the prefix from the class name.
This means that I can rename SMSqueakMap to SM::SqueakMap. The name of the class is still 'SM::SqueakMap' and when I write code I will have to write that - of course. Just like you have to write SMSqueakMap and not just SqueakMap today. Duh. :)
The effect is that we clearly separate the prefix and make it absolutely clear that it IS a prefix. If you now see the class HTTPSocket you can be sure it doesn't have a prefix.
So you ask "Is that all? But, then it is just prefixes but with :: separating it!" - and you are absolutely right.
I find it quite hard to see what could be bad with this change no matter what - prefixes are in use today and wouldn't it just be nice if we had this convention for them? It reads better, you don't have to wonder where the prefix ends or if there is a prefix at all.
But ok, enough of that - what about the rest? There has to be more. The rest of this article describes features in the tools that are completely optional and thus we could even put it as a package on SM for anyone or noone to use, so you don't have to read about it.
If you have accepted the above to be a good thing all on itself then we are all set. If we put that into official Squeak then:
- The total naysayers can continue as before. Using prefixes or not, fine. "Resist change at all costs! Don't touch it I say! Begone you wild creatures of the night with your nasty colons!"
- The people going, "Ok, fine, I buy this prefix separator thingy, it even looks kinda cute and I will start using that. But don't you dear come here and stuffin no darn bloody namespaces down my throat, I hate namespaces and will put the dogs on ya!" are also fine. Perfect.
- And hopefully some will say "Neat. I like it. Now... using this new convention couldn't we now perhaps add some tool support? I mean, now that the tools can actually see the prefixes?". Aha! Goodie.
All the above groups can continue coding as before, they all can use each others code and everyone are happy. After all - all we did was to make it possible to use colons in class names, trivial, right? And people using older Squeaks can actually quite easily file in this little fix and also be able to use code with such class names.
But it turns out most of the issues regarding "namespaces" are tool issues. So I will now present plausible changes to the tools that will make life even better, now that we can see these prefixs. Totally optional tool changes. And we can make all parts in it optional to use with Preferences. So even if you file in the tool part you will be able to shut it off, or even customize it.
Given the above prefixes - what can we do more? Here are some thoughts:
It seems pretty boring typing and reading long class names, couldn't the environment help me? Do I really have to type SM::SqueakMap, isn't 'SqueakMap' enough? I mean, if there is no other class with that short name in the image? And also - when I browse the code - do I really need to see all these prefixes if in fact - the shortnames are unique in the whole image? Can't the browser somehow just show me the shortnames? If I want to that is (a Preference), people loving long names can keep them.
A prefix is actually a namespace you know. And what is a namespace then?
I claim a namespace is EXACTLY and ONLY this: "A named collection of names without duplicates". There is no other intrinsical meaning IMHO. For example people claim that "namespaces should have imports" - says who?
So given that the prefix actually "is" a namespace - we already have namespaces in Squeak - the prefix convention that we already use. Then wouldn't it be nice if we objectified that concept? For no other reason than for manipulating these things easier.
For example, if I want to change prefix on all my SqueakMap classes to ZYP, then... hey, isn't that the same as creating a new "namespace" called 'ZYP' and then moving all SM classes over there? Ah, perhaps like this:
space := Namespace newNamed: #ZYP.
oldSpace := Namespace named: #SM.
oldSpace classes do: [:class | space addClass: class]
Nice. So a Namespace instance is just a prefix "objectified", having its name be the prefix itself. And it is a global. Which means that:
(Namespace named: #SM) == SM
...thus it would be simpler to just do:
Namespace newNamed: #ZYP.
SM classes do: [:class | ZYP addClass: class]
...and the Namespace instance behaves more or less like a Dictionary. Cool. But... what does the Namespace have inside? Nothing! This is a backwards-compatibility trick. All globals still hang in Smalltalk, just like before. The Namespace instance is actually empty and for all its services - acting like a Dictionary - it uses Smalltalk to do the work.
But let's get back to typing and reading code... if I am allowed to type (and read when browsing) only the shortname, what if there are two classes in the image sharing the same shortname? But with different prefixes? Shouldn't the tools help me and ask me which one I mean when I type? And the browser could for those cases show the full name with prefix when I browse, because otherwise I will not know which class it refers to.
And... also, wouldn't it be nice if I could also use a shortname for a class regardless if there are other classes with the same shortname in the image - if and only if the class is in the "local" namespace? With "local" I mean in the same namespace as the code I am writing.
Well, I could go on with other "nice" ideas - but again, these are just tool issues so I think I will stop here.