Handles to Objects
A handle is a pointer to a variable, function, module, or other object. Using a handle lets you write variables or functions that work with the object itself, for example to access its attributes — instead of just its value which is what you usually get when you mention a variable by identifier in an expression.
Viewing handles
In a table result, a handle in an index or content cell usually shows the title of the object. If you select Show by identifier from the Object menu (or press Control+y), it toggles to show identifiers instead of titles (as it does in the node diagrams). If you double-click a cell containing a handle (title or identifier) it opens its Object window (as it does when you double-click a node in a diagram).
Attributes that contain handles
The attributes, inputs, outputs
, and contains
(the list of objects in a module) each consist of a list of handles to objects. The attribute isIn
is a single handle to the module that contains this object — the inverse of contains
.
List of variables: [v1, v2, ... vn]
If you define a variable as a list of variables, for example,
Variable A := [X, Y, Z]
the variable will have a self index that is a list of handles to those variables. In a table result view of A
(or other variable that uses this index), the index A
will usually show the titles of the variables. See List of variables for more. In an expression, the handles in the self index can be accessed using IndexValue(A). The main value of A
(either mid value or a probabilistic view of A
) contains the results of evaluating X
, Y
and Z
.
Handle(o)
Returns a handle to an Analytica object, given its identifier «o».
Handle(Va1) →¨Va1
HandleFromIdentifier(text)
Returns a handle to global object (i.e., not a local variable or parameter), given its identifier as «text». See HandleFromIdentifier()
Variable B := 99
HandleFromIdentifier("B") → Va1
The dependency maintenance is unaware of the dependency on the object. Hence, any changes to the variable B
above will not cause the result to recompute.
ListOfHandles(identifiers...)
Returns a list of handles to the specified Analytica objects, given their «identifiers». See ListOfHandles().
ListOfHandles(Va1, Va2, Va3) →¨ [Va1, Va2, Va3]
Indexes of Handles
MetaOnly attribute
When an index object is defined as a list of identifiers, the MetaOnly
attribute controls whether it is treated as a general index or a meta-index. Meta-indexes are useful when reasoning about the structure or contents of the model itself. A general index evaluates the variables appearing in its definition to obtain its mid or sample value, and the values that are recognized by Subscript (i.e., a[i=x]), while a meta-index (having its MetaOnly
attribute set to 1) does not evaluate the objects in the list. The following comparisons demonstrates the similarities and differences.
Constant E:= exp(1)
Variable X := -1
General Index Meta-Index Index I0 := [E, X, Pi,True]
Index I1 := [E, X, Pi,True]
MetaOnly of I0 := 0 {or not set}
MetaOnly of I1 := 1
Variable A0 := Table(I0)(1, 2, 3, 4)
Variable A1 := Table(I1)(1, 2, 3, 4)
IndexValue(I0) → [E, X, Pi, True]
IndexValue(I1) → [E, X ,Pi, True]
Mid(I0) → 2.718, -1, 3.142,1]
Mid(I1) → [E, X, Pi, True]
A0[I0 = Handle(E)] → 1
A1[I1 = Handle(E)] → 1
A0[I0 = Handle(True)] → 4
A1[I1 = Handle(True)] → 4
A0[I0 = E] → 1
A1[I1 = E] → Error:-2.718 not in I1
A0[I0 = -1] → 2
A1[I1 = -1] → Error:-1 not in I1
A0[I0 = True] → 4
A1[I1 = True] → Error:1 not in I1
MetaIndex..Do
The construct, MetaIndex i := indexExpr
, declares a local meta-index (see Local Indexes). This should generally be used in lieu of the Index i := indexExpr
construct when «indexExpr» evaluates to a list of handles.
MetaIndex I := contains of Revenue_Module;
Description of (I)
IndexesOf(X)
Returns the indexes of an array value as a list of handles. The first element of the list is null, rather than a handle, when «X» has an implicit dimension (also known as a null-index). See also IndexesOf().
Local Variables and Handles
When you declare and use local variables that might hold handles, you should declare these local variables using MetaVar..Do or LocalAlias..Do, rather than Var..Do or For..Do. When local variables hold handles, there are two semantic interpretations — the local variable could either be a storage location for a handle to an object, or it could act as an alias to the object. The MetaVar..Do or LocalAlias..Do declarations makes this distinction clear.
MetaVar..Do
The construct, MetaVar temp := expr
, declares a local variable named «temp» that holds an atomic value or an array of values, some of which might be handles. When the value is a handle, the handle is treated as just another entity, whose datatype happens to be a handle. If you assign another handle or value to «temp», you simply change the contents of the local variable, and do not alter the object that the handle points to. Use this construct to manage collections of handles.
MetaVar hRevModule := Handle(Revenue_module);
MetaVar hExpModule := Handle(Expense_module);
MetaIndex m := [hRevModule, hExpModule];
...
For the next example, suppose a global variable named Hy
exists with a definition of Handle(Y)
.
MetaVar a := Handle(X);
a := Hy
Following the assignment, the local variable a
contains a handle to Y
. The variable X
is unaltered. You should compare this to the same example using LocalAlias..Do in the following section.
When you need to iterate over a collection of handles, you can use, e.g.,
MetaVar h[ ] := Contains of Revenue_module;
You can assign non-handle values, such as numbers or text, to a local variable declared with MetaVar. When you do so, there is no difference between MetaVar..Do and Var..Do.
LocalAlias..Do
The construct, LocalAlias temp := expr
, evaluates «expr» and declares a local variable named «temp» that holds a single atomic value. When «expr» evaluates to a handle, the local variable identifier acts as an exact alias to the object pointed to by the handle.
LocalAlias r := Handle(Rate_input) Do r := r * 1.1
The preceding expression is functionally identical to[1]
Rate_input := Rate_input * 1.1
For the next example, suppose a global variable named Hy
exists with a definition of Handle(Y)
.
LocalAlias a := Handle(X)
a := Hy
This expression is functionally identical to
X := Hy
The expression, if evaluated in a context where side-effects are permitted, would alter the definition of X
. Following the assignment to a
, the local variable a
continues to be an alias to X
. You should compare this to the same example for MetaVar..Do in the preceding section.
When «expr» is an array of handles, then the body expression is iterated for each alias. The following expression increases the definition of every global variable within an indicated module to a value 1.1 times its initial value.
LocalAlias x := Contains of Input_module;
x := x * 1.1
The rule for LocalAlias is that the result for the expressions using «temp» are identical to what you would get if you replaced every occurrence of «temp» within those expressions with the identifier for the variable pointed to by the handle, and then evaluated the expressions[2].
An easy mistake to make when using LocalAlias is to forget that «expr» is evaluated before the assignment. This feature allows you to compute the handle to the object that will be aliased, but it also means that you need to remember to surround an identifier with a call to Handle() when you just want a simple alias. If you write
LocalAlias x := y
then this assigns to «x» the value of «y», not a handle to the object «y». If «y» happens to hold a handle to z
, then «x» becomes an alias to z
, not an alias to «y». To obtain an alias for «y», you must use
LocalAlias x := Handle(y);
You can assign non-handle values, such as numbers and text, to a LocalAlias variable. When you do so, the local variable identifier serves as an alias for a single value, and is the same as
Var temp[] := expr;.
Conversions between MetaVar and LocalAlias
When writing expressions that manipulate handles in local variables, you may sometimes need to convert between MetaVar and LocalAlias semantics. When a MetaVar holds a handle, its value is an entity with the data type of handle. Since subtraction is not defined for a handle, the following expression is an error
MetaVar hr := Handle(Revenue) Do hr - Expenses { Error }
The expression is attempting to subtract the value of expenses (a number) from a handle. In this example, we need the LocalAlias semantics instead, so given hr
we can convert using
MetaVar hr := Handle(Revenue);
LocalAlias r := hr;
r - Expenses
A MetaVar
can also be dereferenced by using the Evaluate() function:
MetaVar hr := Handle(Revenue) Do Evaluate(hr) - Expenses
In the other direction, when a LocalAlias, r
, is an alias for another object and you need the handle to the object, use Handle(r)
. It is legal to use the Handle() function on a local variable declared using LocalAlias, but it is an error to apply the Handle() function to a local variable defined using MetaVar. This is because a local variable is not an object (it is just a name for a value), and since handles can only point to objects, there is no such thing as a handle to a local variable[3].
Notes
- ↑ This expression contains a side-effect to a global variable. Side-effects to global variables are only allowed in expressions or user-defined functions that are run directly from button scripts. This would cause an error if attempted from a variable’s definition.
- ↑ Not every object in Analytica lives in the global namespace (e.g., local indexes), so this rule doesn’t literally apply for handles to objects not in the global namespace. For these, we cannot replace the local name with an identifier since no global identifier exists for the object pointed to by the handle.
- ↑ A local index is an object, so you can have a handle to a local index. The Index..Do and MetaIndex..Do constructs create an index object, along with a local variable name that has LocalAlias semantics for the index object.
Enable comment auto-refresher