[contents]
[prev] [next] [top] [bottom] (3 out of 6)
Module Concepts
Modules provide a mechanism for creating and managing multiple
namespaces, similar to the packaging system in the
Lisp language and the proposed "namespace" mechanism in C++.
Each module has its own global scope, and variables
that are declared global are only global within the boundaries
of that module. The only real global names are the names of
modules themselves.
This section describes the terms and concepts that underlie
the use of modules in ScriptX:
- To export a name is to make a name that is
defined in a given module available for import into other
modules. Only names that are exported are public. Each name
that is to be public must be explicitly exported.
- To import a name is to make a name that has been
exported by another module available for use within a given
module. Therefore, to access a name in another module requires
two steps: that it be exported from that module and imported
into this module. A module can import any name that is public.
- A name binding is an association between a name
and a value cell in memory. A ScriptX module can be thought of
as a collection of name bindings for objects in a program.
- A variable could be described as the set of name
bindings (across modules) for a given value cell in memory. A
variable is defined in only one module, the module in which it
is initially assigned a value. Although a variable is visible
and can be changed in all modules in which it has a name
binding, it can be saved in a storage container only in the
module which defines it.
- A module owns a variable if it defines it by
assigning a value to the name that is associated with the
variable. A definition is a statement of ownership which
combines the declaration of a name and the assignment of a
value to that name.
- Modules are not a security feature. Modules do not control
access to objects in any way. The fact that a module owns a
variable does not prevent a program passing a reference to the
object that variable refers to on to a program that is
compiling in another module. Modules only control access to
objects by name.
As background reading for this section, you might want to
review the ScriptX treatment of scope, lexical names,
variables, and assignment from earlier sections of this
volume.
Exporting Names
Every global name within a module is either exported (public)
or unexported (private). By default, names are not exported.
When you define a module, you specify which names are
exported-that is, which names are available outside the module
for import into other modules. Only names that are exported by
some module can be imported by other modules.
Because a ScriptX variable can store a reference to any
object, including classes and functions, exporting the name of
a class or function makes those objects public, ready for
import into other modules.

Figure 9-1: Exporting names from
modules
A ScriptX module can export a name, even though it does not
define the variable that is associated with that name. If a
module exports names that are declared and defined by one of
its client modules, it acts as an interface for that module.
The section "Organizing Modules" on
page 214 shows how this feature can be used to create a
network of interface and implementation modules.
Importing Names
To access names in another module, you must export those names
from that module and then import them into this module.
Imported names appear in the new module just as if they had
been declared there, and, if they have been defined as
variables, their definitions come along as well. The importing
module is considered to be using the first module, and the
relationship between two modules is called a use relationship.
Modules can use and be used by any number of other modules, as
long as no circular use relationships are created.
In Figure 9-2,
MyOtherModule
uses MyModule
. The
names x
, y
, and z
that
are exported by MyModule
are imported by
MyOtherModule
. The two variables named
x
in the two modules both point to the same value
cell. Thus, if you set x
to a new value in one
module, its value is changed in both modules. Although the
name is exported from one module and imported into the other,
access to its value cell is completely symmetric-it can be set
or get from either module.

Figure 9-2: Importing names into
modules
When you specify that one module uses another module, you can
control which names from that module you want to import. You
can also rename imported names in your own module, to prevent
naming clashes, by prefixing the imported names with a set of
characters or by simply giving the variables entirely
different names. A renamed variable maintains its original
definition. In effect, a ScriptX variable can have a different
name in each module. Finally, imported names can also be
re-exported, effectively "passing them along" to any modules
that use the module that re-exported them.

Figure 9-3: Importing names into a module, with a
prefix
Name Bindings
A module is a collection of name bindings that the compiler
uses at a particular point in the execution of a program.
ScriptX name bindings allow for the separation of names and
objects, so that objects can have different names in different
modules. A different name can be "bound" to a given variable
in each module in which it is visible.
A binding is an association between a name and a value cell in
memory. A value cell stores a pointer to an object somewhere
in memory. Bindings act as a level of indirection between
names and value cells. This indirection is what allows a
ScriptX program to run with different namespaces. When the
ScriptX compiler switches from one module to another, it is
actually switching from one set of bindings to another.
Think of a binding as a way to hang on to an object. Objects
do not have to be associated with bindings. References to
objects are embedded within other objects, often many levels
deep. Expressions that assign a new global name, such as an
assignment, function
, or class
expression, create a binding.
The following example demonstrates what bindings are created
when a simple ScriptX program runs. Although it is not a
complete technical description, it shows in a heuristic way
how objects are associated with names, through bindings, and
how names are used in a ScriptX program to get access to
objects. ScriptX manages a global namespace (there is one
global namespace for each module), by keeping a table of name
bindings.
class KittyCat ()
inst vars
favoriteFood
end
-- create an instances of Cat
object kiri (KittyCat)
settings favoriteFood:"tuna"
end
KittyCat@0xe938c8
This program creates four name bindings. The first one is used
to get access to the newly created KittyCat
class, which is itself an object. The next two bindings are
associated with instance variable access. Instance variable
access is actually through generic getter and setter
functions. These generic functions call the appropriate getter
and setter methods. ScriptX automatically creates entries for
generics in the module's name table. Finally, the script
creates an object, an instance of KittyKat
. This
object is associated with a binding, bringing the total of new
bindings in the system to four.
The following is a list of bindings that have been created so
far in the execution of this program. Names in the first
column are associated with objects, including classes and
generic functions, in the second column. Lexical names are in
lower case because they are interned in their downcase form.
(In the scripter, you don't have to be concerned with case;
you are free to use uppercase and lowercase to make your
scripts more understandable.)
Note that on initialization, the favoriteFood
instance variable slot for the kiri
object is
filled with a string constant (that is, with a pointer to a
StringConstant
object). This string constant has
no bindings of its own. A script can only get and set the
contents of instance variables through some binding or
bindings. A simple instance variable access is actually a
generic function call, which uses the bindings defined for the
object and for the generic functions it provides a method
for.
kiri.favoriteFood
"tuna"
The instance variable access expression
kiri.favoriteFood
is translated into a call to
the getter generic function that is associated with this
instance variable. Two bindings are required to get a
value-one to the generic function and one to the object
itself.
favoriteFoodGetter kiri
Each instance variable is really associated with two name
bindings, one for its getter and one for its setter. If an
instance variable of the same name exists elsewhere in the
current module, it shares the same binding. A generic function
handles method dispatch, routing calls to a getter or setter
to the method defined by the appropriate object. This allows
the same instance variable name to be used by many different
objects within a module.
This chapter discusses syntax for importing and exporting
variables in modules. What really happens in ScriptX is that a
module imports and exports a set of name bindings. As a
result, ScriptX modules do not just share values-they share
value cells.
Defining Variables Within Modules
Each variable can have only one definition across modules,
although that variable can be given a different name when it
is imported into another module. The module that provides a
definition for that module is considered to own that
variable, and only the module that owns it can redefine it.
When a module is stored into the ScriptX object store, all the
variables that it owns and their most recent values are stored
with it.
The only truly global names are module names. All variables
are declared within some module. A variable can only
be accessed (referenced) within some module. Since a given
value cell can be bound to different names in different
modules, and since the same name can be used to denote
different variables in different modules, you do not know what
variable something is until you know what module it was
compiled in.
A variable is owned by the module that
defines it. Only one module owns each variable. The
ScriptX class definition, object definition, function
definition, and global assignment expressions (a global
assignment is a global declaration and assignment all in one
expression) all declare and define a variable. Note that a
variable defined when an object is assigned to a name. This
name is usually declared as a name and assigned a value in the
same expression, as in a class, object, or function
declaration.
A module does not have to be the module that defines a
variable to export a name for that variable. The ScriptX
module system allows variables to be exported from one module,
and then imported and defined in another module. That second
module, the one that defines the variable, does not have to
re-export the variable for that definition to be visible, nor
does a module that wants to use that variable need to use the
module that defined it. By using the module that exported the
variable, its definition comes along automatically. This
separation between an exported variable and its definition
allows you to organize and build networks of modules which can
include circular use relationships between modules, multiple
interfaces to the same module, and more comprehensible
relationships amongst complex networks of modules that use and
export many different variables. "Organizing Modules" on page 214
describes how to use modules in this way.
A single variable can be exported, imported, and renamed in
different modules. Any module with access to that variable can
change its value, if that variable has not been declared
constant. However, only one module is considered to
own that variable. When a module is stored in the
ScriptX object store, it stores all variables it owns with
their current values. If a variable is not owned by any
modules, it cannot be stored with any of those modules.
The module that owns a variable is the module that provides
its definition. A variable that is not defined in any module
is not owned by any module.
The class
, object
, and
function
declaration expressions declare and
assign the variable in one expression, allowing them to be
owned by the module that defined them. You can separate
declaration and assignment for both objects and functions. For
classes, the name must be specified when the class is
declared, and that name is automatically declared constant.
Thus, a class name is always owned by the module in which it
is declared.
A global variable that you create and assign by hand (using an
assignment expression) requires two conditions to be owned by
a module: you must both declare it and assign it in the same
expression:
global myVariable := "variable's value"
Only declaration and assignment in the same expression confer
ownership of a variable; declaring and assigning the variable
in different expressions is not the same:
global anotherVariable
anotherVariable := "another value" -- anotherVariable is not owned
A variable that is declared but not assigned any value does
not get saved with a module-the module does not yet
own that variable. Note that when you define a
variable in a ScriptX program you do not necessarily have to
give it the same value it will hold when the module is stored.
When modules are stored in the ScriptX object store, the
module stores the most recent value for each variable that it
owns. To make sure a variable is owned by a module without
having to assign it its proper value right away, give it a
value of undefined
at declaration time:
global VarToBeChanged := undefined
Later on in the program, you can assign the value you want to
be stored.
A module maintains its own table of name bindings. To export a
variable is to allow other modules to have a binding for a
particular value cell. To say that a particular module defines
a variable means that its value cell was created in that
module. The value cell is created when the variable is
assigned a value. Other modules that have access to the
variable have access only through the value cell in the module
where it is defined. To import a variable into a module is to
have a binding for that variable in the current module. Other
options for using modules, such as excludes
,
prefix
, and renames
, also operate on
this table of bindings.
If a module attempts to define a variable that is already
shared, an error message results and the variable is not
redefined. If a variable is exported (shared), then it is
still only one variable (not a copy) and can have only one
definition. For example, if a shared variable is defined in
one module, assigned to in another module, and read in a third
module, the third module reads the value that was assigned by
the second module.
A shared variable does not have to have the same name in each
module that uses it. You can use features like prefix and
renames to give the variable multiple names, but it is still
only one variable (not a copy) and has only one definition.
Different modules may use different names, but they are still
accessing the same location in memory.
If a variable is not exported, then you can use the same
identifier to define a separate variable in another module.
These variables have the same name within each module, but
they refer to different locations in memory.
In this respect, a variable is really just a collection of
name bindings, one for each module in which the variable is
visible. A variable continues to exist and to occupy memory
when the module it is defined in is no longer the module that
the compiler is currently using. A variable is visible to
scripts in the current module if the current module has a
binding for it.
Examples-Ownership of Variables
ScriptX distinguishes between declaring a name and defining a
variable. In practice the declaration of a name and the
definition of a variable usually occur in the same expression,
but the distinction is important.
The global
expression declares a ScriptX lexical
name. A name that is declared global is visible only within
the module in which it is declared, unless that name is
exported. In this way, the same lexical name can be declared
in different modules.
In the following example, the module Europe
exports four names: finland
,
albania
, portugal
, and
ireland
.
module Europe
uses ScriptX
exports finland, albania, portugal, ireland
end
in module Europe
global finland, albania:19, portugal := 27
When the global expression is combined with an assignment
expression, the resulting expression both declares a lexical
name and defines a variable. Since albania
and
portugal
are assigned values,
albania
and portugal
are both
declared as names and defined as a variables.
At this point, a program can access the values of
albania
and portugal
, since both
names have been defined as variables. Although
finland
is declared as a name, it is not assigned
a value, so finland
is not defined as a variable.
Likewise, ireland
has not been assigned a value,
so it cannot be accessed as a variable. Since
ireland
has not been declared either, assigning a
value to ireland
will cause the compiler to issue
a warning. By contrast, finland
has been
declared, so the compiler does not issue a warning when a
value is assigned to finland
.
If compilation and execution switch to another module that
uses the Europe
module, the variables that are
defined in Europe
are visible there.
module World
uses ScriptX, Europe
end
in module World
albania
19
portugal
27
At this point in the execution of the program,
finland
and ireland
have still not
been defined as variables. When they are assigned values in
the World
module, they are defined in the
World
module. Even though finland
was declared in the Europe
module, and
ireland
was exported by Europe
, but
not declared, both variables are now defined in
World
.
in module World
finland := 11
ireland := 94
If compilation reverts to the Europe
module,
finland
and ireland
retain their
definitions as variables. They are visible in both
Europe
and World
.
in module Europe
finland
11
ireland
94
Although all four of the variables in this example are
exported by Europe
, and three of them are
declared there, it is where they are defined that determines
how they are saved. Since albania
and
portugal
are defined in the Europe module, they
are saved to the object store with Europe
. By
contrast, finland
and ireland
are
actually defined in the World
module.
Only declaration and definition in the same module confers
ownership. Since finland
is not declared in the
World
module, it cannot be owned by any module,
and is not saved. But ireland
, even though it was
exported by Europe
, is declared and defined in
the World
module. Thus, ireland
is
saved with the World
module.
The object, class, and function definition expressions
implicitly declare a global name and define a variable as
well, creating a binding for that variable in the current
module.
module Universe
uses ScriptX
end
in module Universe
class Star () end
object sun (Star) end
function orbit a b ->
format debug "1%* orbits 2%*\n" #(a, b) @normal
In this example, sun
, star
, and
orbit
are declared as lexical names and defined
as variables (a class definition is a constant). Each of these
variables has a binding in the Universe
module,
meaning that a lexical name is associated with the variable in
that module. Since sun
, star
, and
orbit
are defined as variables in
Universe
, they can be saved to the object store
with Universe
, and they can be exported to other
classes.
This document is part of the ScriptX Language Guide, one of the volumes of the ScriptX Technical Reference Series. ScriptX is developed by the ScriptX Engineering Team at Apple Computer, successor to the Kaleida Engineering Team at Kaleida Labs, Inc.
Copyright 1996 Apple Computer, Inc. All Rights Reserved.