[Pharo-dev] Another thought about globals
norbert at hartl.name
Sat Dec 14 05:47:35 EST 2013
> Am 14.12.2013 um 05:41 schrieb Igor Stasenko <siguctua at gmail.com>:
> As you may know, smalltalk global dictionary contain all symbols defined globally,
> so you can access them directly in any piece of code i.e. when you write:
> Object new
> it actually means 'send message #new to object, associated with #Object name in globals dictionary.
> Most of globals are classes, but some of them , like Transcript, World, Display etc are not.
> And i was always thinking there's something wrong with these globals (actually there's multiple 'wrongs'), but finally, i think i can answer myself, what is most basic wrong with them: they miss any form of declaration.
> Most of variables in smalltalk require declaration, such as temps, method arguments, instance variables , class variables, pool variables,
> but not globals.
> Even classes, from formal point of view do not require declaration,
> because actually the usual:
> Object subclass: #Collection
> instanceVariableNames: ''
> classVariableNames: 'MutexForPicking RandomForPicking'
> poolDictionaries: ''
> category: 'Collections-Abstract'
> is _definition_ but not declaration:
> Collection definition =>
> 'Object subclass: #Collection
> instanceVariableNames: ''''
> classVariableNames: ''MutexForPicking RandomForPicking''
> poolDictionaries: ''''
> category: ''Collections-Abstract'''
> in fact, it is just a message sent to 'Object' variable (at some moment in the past) , and there's nothing
> in language which enforces the rule that evaluating such expression must declare new global, named Collection, except from environment we're working in.
> The absence of declaration for globals leads to following problems:
> since declaration point is not defined, all tools (including compiler) assume that given name always been there, and always accessible. Which leads to bootstrap problems.
> There's no way to determine if given piece of code (which uses some global) will keep functioning properly, once you unload certain package. No way to determine dependencies (and as consequence the order of code loading during bootstrapping).
> Also, it is hard to determine, to which package certain global belongs. While it is easy to tell for classes since they having category, for globals like Transcript, Display etc, there's no way to tell anything.
> Piece of cake, you can say: since Display is instance of DisplayScreen class, then such variable must belong to same package as DisplayScreen, right?
> Just for example, imagine i create variable named MyWindowMousePosition, which will contain an instance of Point. Does it means that such variable should belong to same package as Point class? I guess not.
> So, to sum up, i think we should really think how to introduce a way to declare globals in package-based ecosystem, where each global belongs to certain package, and then since packages form dependency hierarchy, you can easily detect whether you allowed to use certain global in given context or not,
> to prevent forming dependency loops.
> But even if we will weaken contract and allow having dependency loops, even in such case declarations can help us to clearly tell which pieces of code will stop functioning properly, if package which declares given variable are not present in system.
> The last aspect of bootstrapping problem is order of initialization,
> because declaring variable doesn't means you can use it right away,
> since it may be not properly initialized yet (otherwise we will be forced to always use lazy initialization pattern).
> From this perspective, IMO package should not only have unordered list of classes/symbols it declares/defines, but also contain information in which order they must be initialized while loaded.
> From other side, i don't really like introducing too much formalism and rules, and keep them as minimal as possible, following smalltalk spirit.
> What you think?
I think packages should be first class citizens. A package once loaded provides an environment/namespace/... that declares all the (exported) symbols that should be accesible global. The smalltalk dictionary should rather be a list of those namespaces/environments/... A package should also have an initialize method where you could specify order of class initialization if necessary ( and other things). Furthermore I would like to see class initializers idempotent. So when loading a package the package initialize is invoked first and then the class initializers, As they are idempotent a possible double invocation is not a problem.
Dependencies are always hard. I would start thinking about metacello defining all the possible dependencies regarding platforms, platform versions etc. At load time there is only one dependency graph and that could be reflected by the system. And this graph is not dependent on the tool that loaded the code.
With this in place unloading should be rather easy. Dependencies can warn the user about an harmful action and when the namespace is removed all global definitions are automaticall removed.
That is what I think. But I also think that there should be a possibility to load something that doesn't end being global. A way to load a package that isn't added to the global namespace (smalltalk dictionary) but to a package namespace would also be very good. Not everything needs to global but we can only load global things. That is a flaw in my opinion. And it prevents cool things like loading multiple versions of a code in separate packages.
More information about the Pharo-dev