A Proposal for Project Layers
amr at whitestein.com
Mon Oct 11 22:08:47 UTC 1999
> I said I would summarize what we're up to with project and name spaces, so here it is. ...
we have implemented very much of what is mentioned here already, at least at a level of a prototype. We could not make some decisions because they ment changes to the language or to the environment (e.g. having .changes files for each namespace/project) - such a decision must be taken by "Squeak generals". The project was interupted for a while in summer (vacations) and is resumed now since ~2 weeks.
> 1. Allow the Smalltalk global dictionary to be extended in a hierarchy. ...
There is a hierarchy of namespaces, Smalltalk is root. Namespaces inherit from each other - multiple dynamic inheritance (can change in time). Namespace is a kind of subsystem, including classes which can be subclasses of classes from other namespaces.
Problem: paradox between specialization and reuse. You are going to specialize e.g. Smalltalk by sub-namespaces "Kernel", "Numeric", "Collections"... which include the particular classes. But then you are going to define a namespace for your application and you want to reuse all of the namespaces.
A. Pure inheritance solution: your application namespace inherit from all "libraries". This builds a lattice with many children for specialization (libraries) and later then with many parents of one "application" namespace for reuse of all these specialzed children. Quite complex graph but simple inheritance mechanizm (easy to track the place where found...).
B. Solution with "import": namespace may import other namespaces (as if they would be a part of it) while keeping their identity (several namespaces may import the same one, changes to it effect all of them). Nice tree-like graph (multiple inheritance is almost not necessary, may be anytime replaced by importing particular namespaces into the super-namespace). Hard to tract inheritance links, hard to understand the real behavior, particularly for larger systems.
We like more the "pure inheritance" approach. However, both of them are implemented already.
> 2. Allow a class to specify its nameSpace. ...
Yes, it is so. Each class _must_ have its namespace - in this namespace all methods are compiled. Default namespace is Smalltalk.
> 3. Allow a project to establish its own binding for the Smalltalk global dictionary.
We have _only_ one Smalltalk in the system. Each project has its namespace. Workspace compiles expressions in this namespace. Environmental browser brows classes of this namespace. Default namespace is Smalltalk.
> 4. Browsers and SystemOrganizers would be extended to handle all of this gracefully, of course. ...
We have left standard tools as they are and implememented new ones - environmental browser (showing classes of particular namespace only), environmental workspace etc.
Categories could be eliminated - this is, however, again a decision which "generals" have to take. So far we have left them in both browsers.
> 5. The mechanism of isolated projects would be adapted .... the outer system is relatively protected from changes made inside the project.
In my opinion, this would violate a bit the clear inheritance between namespaces and the identity of classes and methods. Goes a bit direction "ENVY". But is easily possible - with a kind of mechanizm similar to Undeclared.
> 6. Stores into global variables (and this includes class variables) would be monitored for undoability. I am thinking that stores into Smalltalk would be forbidden except at the level of the current layer.
Here are some of the most general questions. If you are working in a namespace and you are assigning a global variable a new value, but the variable is not local, but inherited from super-namespace - what do you do ? You may rewrite the global variable in the super-namespace, define a local variable with this value or report an error (exception). (I.e. the compiler must generate a bytecode for one of this options)
Possible solution: seting a global variable is not an assignment, but a message send to the context (thisContext, self or project namespace), e.g.
Then, depending on preferences of namespaces, on current situation etc. the context (real context, self or namespace...) can decide what to do and the programmer can gain control over this actions (it is not a bytecode after the compilation).
And this would be a serious change to the Smalltalk language ! An then we cannot stop at global variables. Pool dictionaries, class variables - class acts as a namespace. And this is really so - classes are most atomic namespaces, providing name scope for their methods and contexts. But if we change the assignment of the "capitalized" variables (global, class, pool dict) to message send, why not to change the instance vars? Then we eliminate the assignment from Smalltalk at all (or, better, we can leave it in the syntax, but it would be equivalent to a message sent to the context and it would compile to such a bytecode).
.... forbidding global variable changes (e.g. Smalltalk). This would be a kind of "protected" namespace, which should be dynamic preference of any namespace (e.g. setting some variable of the namespace).
> 7. The current changes file would be layer-related. ...
Yes yes.... Again, we ourselves did not find enough courage to make such a significant change (to have several .changes files, flat or better copying the namespace or project structure in directories - there might be a name conflict....).
Projects, namespaces and classes could store their source code even in a light-weight object dabatase (a kind of ArtBase Personal), not in flat files. Instalation source code for any project/namespace and target system (only "pure" Squeak or a system already including some libraries) could be generated from such a database on demand. Even the whole project/namespace or its classes (each separately) could be stored in such a database and loaded on demand (e.g. upon doesNotUnderstand: of some of its instances) - in this way works ArtBASE which is able to store also classes and methods and load them transparently when they are needed.
> 8. Essentially all of this should work without qualified variable names. But .... "ThingLab.Line" or "Global.Rectangle", ...
I never liked the "dotted notation" (although I have started with Simula before Smalltalk). How about a normal message send:
^Global Rectangle origin: 0 at 0 corner: 100 at 100.
The first token "Global" is either a global variable in current namespace (including inherited) (i.e. value of the association), or a message send to thisContext or self which returns the same, however, by resolving this in run-time.Next token "Rectangle" is a message send to the first object - this causes a real-time look-up for the object which "Global" (probably a namespace) understands under "Rectangle" (probably a class). This object receives the next message....
I vote for resolving global variables in runtime as message sends to thisContext. I vote against "dotted notation". If the Smalltalk community doesn't like message sends for identifing the receiver and prefers early binding for global variables, let's rather use "slashed" notation Global/Rectangle which would be resolved by compiler in compile-time (for globals within the namespaces only). This would be also compatible with the directory structure of .changes files for project and namespace hierarchy. I.e. global variables would be either "full qualified names" like "Kernel/Object/Boolean" with Smalltalk as default root, or relative "Boolean"
which could be also overridden within some other namespace (seeing another Boolean class).
......Unless we want to start using the dotted notation for identifying all the message sends in the way like
Global.Rectangle.newOriginCorner(0 at 0; 100 at 100)
More information about the Squeak-dev