Skip to main content
Engineering LibreTexts

6.9: Shared Variables

  • Page ID
    40127
    \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Now we will look at an aspect of Pharo that is not so easily covered by our five rules: shared variables.

    Pharo provides three kinds of shared variables:

    1. Globally shared variables.
    2. Class variables: variables shared between instances and classes. (Not to be confused with class instance variables, discussed earlier).
    3. Pool variables: variables shared amongst a group of classes,

    The names of all of these shared variables start with a capital letter, to warn us that they are indeed shared between multiple objects.

    Global variables

    In Pharo, all global variables are stored in a namespace called Smalltalk, which is implemented as an instance of the class SystemDictionary. Global variables are accessible everywhere. Every class is named by a global variable. In addition, a few globals are used to name special or commonly useful objects.

    The variable Processor names an instance of ProcessScheduler, the main process scheduler of Pharo.

    Processor class
    >>> ProcessorScheduler
    

    Other useful global variables

    Smalltalk is the instance of SmalltalkImage. It contains many functionality to manage the system. In particular it holds a reference to the main namespace Smalltalk globals. This namespace includes Smalltalk itself since it is a global variable. The keys to this namespace are the symbols that name the global objects in Pharo code. So, for example:

    Smalltalk globals at: #Boolean
    >>> Boolean
    

    Since Smalltalk is itself a global variable:

    Smalltalk globals at: #Smalltalk
    >>> Smalltalk
    
    (Smalltalk globals at: #Smalltalk) == Smalltalk
    >>> true
    

    World is an instance of PasteUpMorph that represents the screen. World bounds answers a rectangle that defines the whole screen space; all Morphs on the screen are submorphs of World.

    ActiveHand is the current instance of HandMorph, the graphical representation of the cursor. ActiveHand’s submorphs hold anything being dragged by the mouse.

    Undeclared is another dictionary, which contains all the undeclared variables. If you write a method that references an undeclared variable, the browser will normally prompt you to declare it, for example as a global or as an instance variable of the class. However, if you later delete the declaration, the code will then reference an undeclared variable. Inspecting Undeclared can sometimes help explain strange behaviour!

    Using globals in your code

    The recommended practice is to strictly limit the use of global variables. It is usually better to use class instance variables or class variables, and to provide class methods to access them. Indeed, if Pharo were to be implemented from scratch today, most of the global variables that are not classes would be replaced by singletons.

    The usual way to define a global is just to perform Do it on an assignment to a capitalized but undeclared identifier. The parser will then offer to declare the global for you. If you want to define a global programmatically, just execute Smalltalk globals at: #AGlobalName put: nil. To remove it, execute Smalltalk globals removeKey: #AGlobalName.

    Class variables

    Sometimes we need to share some data amongst all the instances of a class and the class itself. This is possible using class variables. The term class variable indicates that the lifetime of the variable is the same as that of the class. However, what the term does not convey is that these variables are shared amongst all the instances of a class as well as the class itself, as shown in Figure \(\PageIndex{1}\). Indeed, a better name would have been shared variables since this expresses more clearly their role, and also warns of the danger of using them, particularly if they are modified.

    Instance and class methods accessing different variables.
    Figure \(\PageIndex{1}\): Instance and class methods accessing different variables.

    In Figure \(\PageIndex{1}\) we see that rgb and cachedDepth are instance variables of Color, hence only accessible to instances of Color. We also see that superclass, subclass, methodDict and so on are class instance variables, i.e., instance variables only accessible to the Color class.

    But we can also see something new: ColorRegistry and CachedColormaps are class variables defined for Color. The capitalization of these variables gives us a hint that they are shared. In fact, not only may all instances of Color access these shared variables, but also the Color class itself, and any of its subclasses. Both instance methods and class methods can access these shared variables.

    A class variable is declared in the class definition template. For example, the class Color defines a large number of class variables to speed up color creation; its definition is shown below.

    Object subclass: #Color
        instanceVariableNames: 'rgb cachedDepth cachedBitPattern alpha'
        classVariableNames: 'BlueShift CachedColormaps ColorRegistry
            ComponentMask GrayToIndexMap GreenShift HalfComponentMask
            IndexedColors MaskingMap RandomStream RedShift'
        package: 'Graphics-Primitives'
    

    The class variable ColorRegistry is an instance of IdentityDictionary containing the frequently-used colors, referenced by name. This dictionary is shared by all the instances of Color, as well as the class itself. It is accessible from all the instance and class methods.

    Class initialization

    The presence of class variables raises the question: how do we initialize them?

    One solution is lazy initialization (discussed earlier in this chapter). This can be done by introducing an accessor method which, when executed, initializes the variable if it has not yet been initialized. This implies that we must use the accessor all the time and never use the class variable directly. This furthermore imposes the cost of the accessor send and the initialization test. It also arguably defeats the point of using a class variable, since in fact it is no longer shared.

    Another solution is to override the class method initialize (we’ve seen this before in the Dog example).

    Color class >> initialize
        ...
        self initializeColorRegistry.
        ... 
    

    If you adopt this solution, you will need to remember to invoke the initialize method after you define it (by evaluating Color initialize). Although class side initialize methods are executed automatically when code is loaded into memory (from a Monticello repository, for example), they are not executed automatically when they are first typed into the browser and compiled, or when they are edited and re-compiled.

    Pool variables

    Pool variables are variables that are shared between several classes that may not be related by inheritance. Pool variables were originally stored in pool dictionaries; now they should be defined as class variables of dedicated classes (subclasses of SharedPool). Our advice is to avoid them; you will need them only in rare and specific circumstances. Our goal here is therefore to explain pool variables just enough so that you can understand them when you are reading code.

    A class that accesses a pool variable must mention the pool in its class definition. For example, the class Text indicates that it is using the pool dictionary TextConstants, which contains all the text constants such as CR and LF. This dictionary has a key #CR that is bound to the value Character cr, i.e., the carriage return character.

    ArrayedCollection subclass: #Text
        instanceVariableNames: 'string runs'
        classVariableNames: ''
        poolDictionaries: 'TextConstants'
        package: 'Collections-Text'
    

    This allows methods of the class Text to access the keys of the dictionary in the method body directly, i.e., by using variable syntax rather than an explicit dictionary lookup. For example, we can write the following method.

    Text >> testCR
        ^ CR == Character cr
    

    Once again, we recommend that you avoid the use of pool variables and pool dictionaries.


    This page titled 6.9: Shared Variables is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.