5.7: Shared Variables
- Page ID
- 37702
\( \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}}\)
\( \newcommand{\vectorA}[1]{\vec{#1}} % arrow\)
\( \newcommand{\vectorAt}[1]{\vec{\text{#1}}} % arrow\)
\( \newcommand{\vectorB}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vectorC}[1]{\textbf{#1}} \)
\( \newcommand{\vectorD}[1]{\overrightarrow{#1}} \)
\( \newcommand{\vectorDt}[1]{\overrightarrow{\text{#1}}} \)
\( \newcommand{\vectE}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash{\mathbf {#1}}}} \)
\( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \)
\( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)
\(\newcommand{\avec}{\mathbf a}\) \(\newcommand{\bvec}{\mathbf b}\) \(\newcommand{\cvec}{\mathbf c}\) \(\newcommand{\dvec}{\mathbf d}\) \(\newcommand{\dtil}{\widetilde{\mathbf d}}\) \(\newcommand{\evec}{\mathbf e}\) \(\newcommand{\fvec}{\mathbf f}\) \(\newcommand{\nvec}{\mathbf n}\) \(\newcommand{\pvec}{\mathbf p}\) \(\newcommand{\qvec}{\mathbf q}\) \(\newcommand{\svec}{\mathbf s}\) \(\newcommand{\tvec}{\mathbf t}\) \(\newcommand{\uvec}{\mathbf u}\) \(\newcommand{\vvec}{\mathbf v}\) \(\newcommand{\wvec}{\mathbf w}\) \(\newcommand{\xvec}{\mathbf x}\) \(\newcommand{\yvec}{\mathbf y}\) \(\newcommand{\zvec}{\mathbf z}\) \(\newcommand{\rvec}{\mathbf r}\) \(\newcommand{\mvec}{\mathbf m}\) \(\newcommand{\zerovec}{\mathbf 0}\) \(\newcommand{\onevec}{\mathbf 1}\) \(\newcommand{\real}{\mathbb R}\) \(\newcommand{\twovec}[2]{\left[\begin{array}{r}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\ctwovec}[2]{\left[\begin{array}{c}#1 \\ #2 \end{array}\right]}\) \(\newcommand{\threevec}[3]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\cthreevec}[3]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \end{array}\right]}\) \(\newcommand{\fourvec}[4]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\cfourvec}[4]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \end{array}\right]}\) \(\newcommand{\fivevec}[5]{\left[\begin{array}{r}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\cfivevec}[5]{\left[\begin{array}{c}#1 \\ #2 \\ #3 \\ #4 \\ #5 \\ \end{array}\right]}\) \(\newcommand{\mattwo}[4]{\left[\begin{array}{rr}#1 \amp #2 \\ #3 \amp #4 \\ \end{array}\right]}\) \(\newcommand{\laspan}[1]{\text{Span}\{#1\}}\) \(\newcommand{\bcal}{\cal B}\) \(\newcommand{\ccal}{\cal C}\) \(\newcommand{\scal}{\cal S}\) \(\newcommand{\wcal}{\cal W}\) \(\newcommand{\ecal}{\cal E}\) \(\newcommand{\coords}[2]{\left\{#1\right\}_{#2}}\) \(\newcommand{\gray}[1]{\color{gray}{#1}}\) \(\newcommand{\lgray}[1]{\color{lightgray}{#1}}\) \(\newcommand{\rank}{\operatorname{rank}}\) \(\newcommand{\row}{\text{Row}}\) \(\newcommand{\col}{\text{Col}}\) \(\renewcommand{\row}{\text{Row}}\) \(\newcommand{\nul}{\text{Nul}}\) \(\newcommand{\var}{\text{Var}}\) \(\newcommand{\corr}{\text{corr}}\) \(\newcommand{\len}[1]{\left|#1\right|}\) \(\newcommand{\bbar}{\overline{\bvec}}\) \(\newcommand{\bhat}{\widehat{\bvec}}\) \(\newcommand{\bperp}{\bvec^\perp}\) \(\newcommand{\xhat}{\widehat{\xvec}}\) \(\newcommand{\vhat}{\widehat{\vvec}}\) \(\newcommand{\uhat}{\widehat{\uvec}}\) \(\newcommand{\what}{\widehat{\wvec}}\) \(\newcommand{\Sighat}{\widehat{\Sigma}}\) \(\newcommand{\lt}{<}\) \(\newcommand{\gt}{>}\) \(\newcommand{\amp}{&}\) \(\definecolor{fillinmathshade}{gray}{0.9}\)Now we will look at an aspect of Smalltalk that is not so easily covered by our five rules: shared variables.
Smalltalk provides three kinds of shared variables: (1) globally shared variables; (2) variables shared between instances and classes (class variables), and (3) variables shared amongst a group of classes (pool variables). 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 Squeak, 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 Transcript
names an instance of TranscriptStream
, a stream that writes to a scrolling window. The following code displays some information and then goes to the next line in the Transcript
.
Transcript show: 'Squeak is fun and powerful' ; cr
Before you do it
, open a transcript by dragging one from the Tools flap.
Hint
Writing to the Transcript is slow, especially when the transcript window is open. So, if you experience some sluggishness and are writing to the Transcript, think about collapsing it.
Other useful global variables
Smalltalk
is the instance ofSystemDictionary
that defines all of the globals — includingSmalltalk
itself. The keys to this dictionary are the symbols that name the global objects in Smalltalk code. So, for example,Smalltalk at: #Boolean → Boolean
Since
Smalltalk
is itself a global variable,Smalltalk at: #Smalltalk → a SystemDictionary(lots of globals)}
and
(Smalltalk at: #Smalltalk) == Smalltalk → true
Sensor
is an instance ofEventSensor
, and represents input to Squeak. For example,Sensor keyboard
answers the next character input on the keyboard, andSensor leftShiftDown
answerstrue
if the left shift key is being held down, whileSensor mousePoint
answers aPoint
indicating the current mouse location.World
is an instance ofPasteUpMorph
that represents the screen.World bounds
answers a rectangle that defines the whole screen space; all Morphs on the screen are submorphs ofWorld
.ActiveHand
is the current instance ofHandMorph
, the graphical representation of the cursor.ActiveHand
’s submorphs hold anything being dragged by the mouse.Undeclared
is another dictionary — it 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. InspectingUndeclared
can sometimes help explain strange behaviour!SystemOrganization
is an instance ofSystemOrganizer
: it records the organization of classes into packages. More precisely, it categorizes the names of classes, soSystemOrganization categoryOfElement: #Magnitude → #'Kernel-Numbers'
Current 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 Squeak 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 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 at: #AGlobalName put: nil
. To remove it, execute Smalltalk 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.
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: ColorNames
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 (class 5.20).
Code \(\PageIndex{1}\) (Squeak): Color and Its Class Variables
Object subclass: #Color instanceVariableNames: 'rgb cachedDepth cachedBitPattern' classVariableNames: 'Black Blue BlueShift Brown CachedColormaps ColorChart ColorNames ComponentMask ComponentMax Cyan DarkGray Gray GrayToIndexMap Green GreenShift HalfComponentMask HighLightBitmaps IndexedColors LightBlue LightBrown LightCyan LightGray LightGreen LightMagenta LightOrange LightRed LightYellow Magenta MaskingMap Orange PaleBlue PaleBuff PaleGreen PaleMagenta PaleOrange PalePeach PaleRed PaleTan PaleYellow PureBlue PureCyan PureGreen PureMagenta PureRed PureYellow RandomStream Red RedShift TranslucentPatterns Transparent VeryDarkGray VeryLightGray VeryPaleRed VeryVeryDarkGray VeryVeryLightGray White Yellow' poolDictionaries: '' category: 'Graphics-Primitives'
The class variable ColorNames
is an array containing the name of frequently-used colors. This array is shared by all the instances of Color
and its subclass TranslucentColor
. It is accessible from all the instance and class methods.
ColorNames
is initialized once in Color class»initializeNames, but it is accessed from instances of Color
. The method Color»name uses the variable to find the name of a color. Since most colors do not have names, it was thought inappropriate to add an instance variable name to every color.
Class initialization
The presence of class variables raises the question: how do we initialize them? One solution is lazy initialization. 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.
Code \(\PageIndex{2}\) (Squeak): Color class»colorNames
Color class»colorNames ColorNames ifNil: [self initializeNames]. ↑ ColorNames
Another solution is to override the class method initialize
.
Code \(\PageIndex{3}\) (Squeak): Color class»initialize
Color class»initialize ... self initializeNames
If you adopt this solution, you need to remember to invoke the initialize
method after you define it, e.g., by evaluating Color initialize
. Although class side initialize
methods are executed automatically when code is loaded into memory, 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.
Code \(\PageIndex{4}\) (Squeak): Pool Dictionaries in the Text Class
ArrayedCollection subclass: #Text instanceVariableNames: 'string runs' classVariableNames: '' poolDictionaries: ’TextConstants’ category: '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.
Code \(\PageIndex{5}\) (Squeak): Text»testCR
Text»testCR ↑ CR == Character cr
Once again, we recommend that you avoid the use of pool variables and pool dictionaries.