Namespaces
New to Analytica 6.5, and still experimental, not fully exposed, unsupported and subject to change.
This page describes a feature that is only partially implemented (as of Analytica 6.5 beta). Despite being incomplete, it is in a state where it is useful for some purposes, including Lumina-internal development, and hence is documented here. It can be extremely useful, but may be subject to change between now and feature completion.
What is a Namespace?
A global identifier is a name for a global object. Each global object belongs to a namespace and within that namespace, its identifier must be unique. For example, you cannot have two objects both named X
in the same namespace. In fact, if you try to create a second object named X
in the same namespace, Analytica will change it to be unique by adding numbers to the end, for example by changing the new name to X1
. However, you can create a second object named X
in a distinct namespace,
An object with a module or library class or subclass can be configured to be a namespace. An object of one of these classes is not namespaces normally a separate namespace, so it must be explicitly configured to be so. Filed modules and libraries are automatically configured to be namespaces, and an object with class Model is automatically a namespace.
Internally, the presence of the NamespaceDefaultScope attribute on a module subclass with a value of either Public
or Private
is what actually determines whether a module object is a namespace. Modules or libraries without this attribute are not namespaces, whereas those with this attribute are. The attribute value must be either Public
, Private
or Not A Namespace
, with the meaning of this value is described below.
In addition to your model's namespace(s), there is also a system (or root) namespace which holds the built-in functions, system variables, etc. Analytica will usually add numbers to the end of your identifier it collides with an identifier in the system namespace, but in theory that CAN co-exist. When a system object is added to Analytica in a new release, collisions with existing legacy models do arise, and in this case Analytica allows both to have the same name.
Use Cases
When you have multiple people working on the same model, it is common to partition the work into Modules. Two people may end up creating variables within their own module that have the same name, but which might not be actual duplicates of the same quantity. When you merge the work together, this results in identifier collisions which must be dealt with. One nice solution is to configure each person's module to be a namespace so that these duplicate names don't actually collide, eliminating the need to rename someone's variables.
When you create a library to be used by others, you have no way to know that eventual users will never duplicate an identifier inside your library. Hence, it makes a lot of sense for your libraries to be namespaces. If fact, this is so compelling that Analytica will do this automatically when you Add Library... or Add Module... or use a filed module.
Qualified Names
When there are multiple identifiers with the same name, each in a different namespace, you may need to disambiguate which object you want by using a qualified namein the form NS :: X
. For example, suppose NS1
and NS2
are names of two distinct namespaces, each containing a different object named X
. Then you could use NS1 :: X
or NS2 :: X
to disambiguate which object you want.
Namespace names are themselves identifiers and thus subject to the same properties of other identifiers -- i.e., two namespaces might have the same name when each is in a distinct namespace. Thus, you might use a qualified name to get to the namespace of interest, resulting in a namespace path such as: NS1 :: NS2 :: X
.
The operator ::
is called the namespace operator. It has the highest precedence and cannot be broken up by parentheses. It must contain an identifier, qualified identifier or nothing on its left, and an identifier on its right.
You can begin a qualified name at the root/system namespace by starting out with ::
such as ::Npv( cash_flow, Time, 5%)
. When you start at the root, it will match system objects first, and then objects that are in scope in your model namespace.
Each non-terminal component of a qualified name must be the identifier of a namespace that is in scope within the namespace identified by the path up to that point. These cannot be identifiers for modules that are not configured to be namespaces. However, there is one exception to this. When creating a new object using the typescript syntax
- «className» «newIdentifier»
the «newIdentifier» can specify the parent module as the penultimate component of the path. For example,
Decision Sales :: Incentive_discount
would create a decision variable named Incentive_discount
inside the module Sales
, and Sales
does not itself need to be a namespace.
In many cases you can use an unqualified identifier to access an object that resides in a different namespace. This is possible when the identifier is unambiguously imported or is inherited by the current namespace, which are described below.
Namespace Imports
A namespace, say NS1
, can import another namespace, NS2
, which makes all the public identifiers in NS2
visible from NS1
without having to use a qualified path.
You specify a namespace import by adding the imported namespace's identifier (or qualified identifier) to the receiving namespace's NamespaceImports
attribute. For example, in typescript:
NamespaceImports NS1 : NS2
If NS1
imports multiple namespaces, they should be listed one on each line, e.g.,
NamespaceImports NS1 : NS2~ NS3~ NS4::NS5
In the above example, the identifiers directly in NS4
are not imported but those in NS5
are.
A NamespaceImports attribute has no effect (is ignored) when it is set for a non-namespace object.
When a child (or grand child) module is configured to be a namespace, the parent module does not automatically import the identifiers is those submodules. You must add the child module to the parent's NamespaceImports
attribute in order to use the identifiers inside the submodule without a qualified path. Note that this is different from the case where the child module is not configured to be a namespace, since in that case, all the objects in the child actually belong to the parent's namespace. (Note: In the future, when namespace features are exposed on the Analytica GUI, it'll automatically add the import to the parent namespace when you first configure a module to be namespace, thus requiring you to remove the import if you don't want that. At present, since you have to configure namespaces by setting attribute values, you need to do both.).
What one namespace imports multiple other namespaces, identifier ambiguity can arise.
Enable comment auto-refresher