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
LocalIndex..Do
The construct, LocalIndex i := indexExpr
, declares a local meta-index (see Local Indexes). This should generally be used in lieu of the Index Index i := indexExpr
construct when «indexExpr» evaluates to a list of handles.
LocalIndex I := Contains of Revenue_Module;
Description of (I)
See more at Local..Do
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 Local..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 Local..Do or LocalAlias..Do declarations makes this distinction clear.
Local..Do
The construct, Local 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.
Local hRevModule := Handle(Revenue_module);
Local hExpModule := Handle(Expense_module);
Local m := [hRevModule, hExpModule];
...
For the next example, suppose a global variable named Hy
exists with a definition of Handle(Y)
.
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.,
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
Local temp[] := expr;
Conversions between Local and LocalAlias
When writing expressions that manipulate handles in local variables, you may sometimes need to convert between Local and LocalAlias semantics. When a Local 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
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
Local hr := Handle(Revenue);
LocalAlias r := hr;
r - Expenses
A Local can also be dereferenced by using the Evaluate() function:
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 Local. 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 LocalIndex..Do and Index..Do constructs create an index object, along with a local variable name that has LocalAlias semantics for the index object.
See Also
Enable comment auto-refresher