Difference between revisions of "Handle Functions"

 
(18 intermediate revisions by 4 users not shown)
Line 1: Line 1:
 
[[Category:Meta-Inference Functions]]
 
[[Category:Meta-Inference Functions]]
[[Category:Doc Status C]] <!-- For Lumina use, do not change -->
 
  
[[What's new in Analytica 4.0?]] >
+
{{ReleaseBar}}
  
= About Handles (varTerms) =
+
A '''handle''' is a reference or pointer to a variable, index, function, module, or other Analytica object. Using a handle lets you write variables or functions that work with the object itself rather than the value of the object. 
  
A '''handle''' (formerly known as '''varTerm''') is a reference or pointer to a variable, index, function, module, or other Analytica object. Using a handle lets you write variables or functions that works with the object itself rather than the value of the object. Usually, if you refer to variable X in an expression, say as:
+
== About handles (varTerms) ==
Variable X := 100
+
A handle is refers to a variable rather than the value of the variable. It's useful when we want to operate on the variable as an object -- for example, to see or modify one of its attributes.   
Variable A := X
 
The value of A will be equal to the value of X, namely 100. But if you write:
 
  Variable B := Handle(X)
 
  
B now refers to variable X, and you can now access the Title, Description, Inputs or Outputs of X. You can also create and use lists of handles to objects. For example, the list of indexes of an array, or the inputs or outputs of a variable.
+
If you refer to variable <code>X</code> in an expression, say,
 +
:<code>Variable X := 100</code>
 +
:<code>Variable A := X</code>
  
If you define a variable as a list of variables:
+
The value of <code>A</code> will be equal to the value of <code>X</code>, namely 100. But if you write:
Variable A := [X, Y, Z]
+
:<code>Variable B := Handle(X)</code>
the value of A will be the result of evaluating X, Y, and Z. The index of A is a list of handles to X, Y, and Z.  It usually shows the titles of X, Y, and Z. If you double-click the title (or identifier) of an object in the index in a Table window, it opens the Object view for that object.  If "Show By Identifier" is on (from the Object menu, or if you toggle it with control-Y), the index shows identifiers rather than titles. Either way, you can double click to open the object view.
 
  
Some attributes consist of lists of handles, notably Inputs, Outputs, and Contains (the objects in a module).  You can use these to write functions to find the ancestors, descendants, or contents of an object.
+
<code>B</code> now refers to variable <code>X</code>, and you can now access the <code>Title, Description, Inputs</code> or <code>Outputs</code> of <code>X</code>. For example,
 +
:<code>Identifier OF (B) --> X</code>
  
= Handle(v) =
+
Sometimes it's useful to have a list of handles to objects. For example, the Outputs or Inputs attribute of a variable returns a list of handles to variables and other objects.
  
Returns a handle (pointer or reference) to object v. 
+
If you define a variable as a list of variables:
 +
:<code>Variable A := [X, Y, Z]</code>
  
  Function Handle( X : Variable ; AsIndex : optional boolean atomic )
+
the value of <code>A</code> will be an array containing the results of evaluating the variables <code>X, Y</code>, and <code>Z</code>. The result will have <code>A</code> an oneIndex (the [self Index]), which is a list of handles to <code>X, Y</code>, and <code>Z</code>. It will also have the union of all the indexes of <code>X, Y</code>, and <code>Z</code>.
  
If you want to create a user-defined function that returns a Handle, in most cases the return value needs to involve a call to the Handle function.  If you simply place the identifier of a local variable (even if it declared as Variable) as the return value, it will be evaluated. For example, consider these two functions:
+
When displaying the array as a table, the Index A (usually) shows the titles of <code>X, Y</code>, and <code>Z</code>. The titles are blue, indicating that you can click each item to open the '''Object''' view for that object.  If "Show By Identifier" is on (from the '''Object''' menu, or if you toggle it with ''control-Y''), the index shows identifiers rather than titles. Either way, you can double click to open the object view.
  
Function Fu1( X : Variable ) := X
+
Some attributes consist of lists of handles, notably <code>Inputs, Outputs</code>, and <code>Contains</code> (the objects in a module). The attribute <code>Isin</code> is a handle to the Module containing this object. You can use these to write functions to find the ancestors, descendants, parents or contents of an object.
Function Fu2( X : Variable ) := Handle(X)
 
  Variable Va1 := 5
 
  
Suppose you evaluate Fu1(Va1) and Fu2(Va1).  Fu1 returns the value of Va1, that is 5.  Fu2 returns a handle to the Va1 object.  Hence, to return a Handle, a call to Function Handle was necessary.
+
== Handle(v) ==
+
Returns a handle (pointer or reference) to object «v».  
You can declare a local variable to be a Handle using, for example,
 
var a := Handle(Va1) do ...
 
where Va1 is the identifier of an object. In the scope of this var declaration, a then becomes an alias for Va1.  In other words, anywhere you might use the identifier Va1 in an expression, you could equivalently use a. 
 
  
To create an alias to an index, use
+
[[Syntax]]:
var I := Handle( In1, AsIndex:True ) do ...
+
:'''Handle'''(v: Variable; AsIndex: optional boolean atomic)
The local variable I will then serve as an alias to the original index In1 inside the scope of this var statement.  The AsIndex parameter causes I's index values to be used when I appears in a value context (this is relevant when the original object is a self-indexed array, having both a value and an index value).
 
  
When an index has handles as elements, and you wish to subscript across that index, the recommended syntax is:
+
If you want to create a user-defined function that returns a <code>Handle</code>, in most cases the return value needs to involve a call to the [[Handle]] function.  If you simply place the identifier of a local variable (even if it declared as <code>Variable</code>) as the return value, it will be evaluated. For example, consider these two functions:
  A[ I = Handle(x) ]
 
In this example, x would be the identifier appearing as one of the elements of I.
 
  
= Function ListOfHandles =
+
:<code>Function Fu1(X: Variable) := X</code>
 +
:<code>Function Fu2(X: Variable) := Handle(X)</code>
 +
:<code>Variable Va1 := 5</code>
  
  ListOfHandles( identifiers : repeated Object )
+
Suppose you evaluate <code>Fu1(Va1)</code> and <code>Fu2(Va1)</code>.  <code>Fu1</code> returns the value of <code>Va1</code>, that is 5.  <code>Fu2</code> returns a handle to the <code>Va1</code> object. Hence, to return a <code>Handle</code>, a call to Function [[Handle]] was necessary.
 +
 +
You can declare a local to be a Handle using, for example,
 +
:<code>{{MetaVar}} h := Handle(Va1) do ...</code>
  
''new to Analytica 4.3''
+
where <code>Va1</code> is the identifier of an object.  In this case, «h» holds a handle. It is not an alias to <code>Va1</code>, but it does serve to identify that object. You can have arrays of handles, and you can have many handles to the same object. When you later assign a different handle to <code>h</code>, the object <code>Va1</code> does not change, but the local <code>h</code> will then hold a different handle.
  
Returns a list of handles to several objects specified by their identifiers.  For example:
+
You can alternatively declare a local as assign it a handle using, for example
  ListOfHandles( X,Y,Z )
+
:<code>[[LocalAlias]] a := Handle(Va1) do...</code>
 +
In this case, within the scope of this [[LocalAlias]] declaration, «a» then becomes an alias for <code>Va1</code>. In other words, anywhere you might use the identifier <code>Va1</code> in an expression, you could equivalently use <code>a</code>. 
  
returns a list with three elements, the first item being a handle to X, etc.  The end result is similar to the result of evaluating:
+
To create an alias to an index, use
[Handle(X),Handle(Y),Handle(Z)]
+
:<code>[[LocalAlias]] I := [[Handle]](In1, AsIndex: True) do ..</code>
  
but of course is more convenientHowever, there is another distinction. When you define a variable using the explicit list syntax, [Handle(X),...], the variable ends up being a 1-D self-indexed array, where the index elements are each an expression, such as Handle(X), the value is the handle itself.  In some scenarios that is nice, in that is shows you where the result came from when viewing a result table, but when defining an index you often want the index values to be the handles themselves.  Using ListOfHandles(..) directly accomplishes this.
+
The local identifier <code>I</code> will then serve as an alias to the original index <code>In1</code> inside the scope of this var statementThe <code>AsIndex</code> parameter causes <code>I</code>'s index values to be used when <code>I</code>. appears in a value context (this is relevant when the original object is a self-indexed array, having both a value and an index value).
  
= Function HandleFromIdentifier =
+
When an index has handles as elements, and you wish to subscript across that index, the recommended syntax is:
 +
:<code>A[I = Handle(x)]</code>
  
HandleFromIdentifier( varName : atomic text )
+
In this example, <code>x</code> would be the identifier appearing as one of the elements of <code>I</code>.
  
(formerly GetVariableByName).
+
==ListOfHandles(x1, ''x2,..,xn'') ==
 +
[[Syntax]]:
 +
:'''ListOfHandles'''(identifiers: Repeated Object)
  
Finds an object in the global namespace having the indicated name and returns a handle to that objectIf no such object exists, returns Null (use IsUndef to test for null if you worry about backward compatibility).
+
Returns a list of handles to one or more objects specified by their identifiersFor example:
 +
:<code>ListOfHandles('X', 'Y', 'Z')</code>
  
One danger with using this function is that Analytica has no dependency influence information.  For example, the following expression
+
returns a list of handles to the three variablesYou get the same result from:
  var x := HandleFromIdentifier("Va1") do x
+
:<code>[Handle(X), Handle(Y), Handle(Z)]</code>
evaluates Va1 and returns that value; however, if Va1 were to change, Analytica would have no way of knowing that this result needs to be invalidatedObviously, in this example, it would better to use just the identifier Va1 in the expression, but as a basis of comparison, note that Analytica would be aware of the dependency influence if this variation were used:
 
  var x:= Handle(Va1) do x
 
If the value of Va1 changes, it would know that this value needs to be re-computed.
 
  
== Handle to Caller of a [[User-Defined Functions|UDF]] ==
+
but the first is usually more convenient.  There is another distinction.  If you assign the first example, using  [[ListOfHandles]], to an Index, it will uses actual handles, which show as clickable blue links containing the title of each object in a table. If you assign the second example, using a simple list, to an Index, each element in the index will appear as the expression, Handle(X), etc, which is not usually what you want.
  
(note: This does not need to be mentioned in the user-guide)
+
== HandleFromIdentifier(x) ==
 +
[[Syntax]]:
 +
:'''HandleFromIdentifier'''(varName : atomic text)
  
A special case, HandleFromIdentifier("_Caller"), can be used in a user-defined function to obtain a handle to the variable, function, or button that invoked the functionFrom a nested function, the variable or button that invoked the parent function is returned.  The tag "_Caller" will not conflict with an identifier since identifiers may not begin with an underscore.
+
Returns a handle to a variable or other object with the identifier «varName»If no such object exists, it returns [[Null]].
  
Using this, it is possible to create a function that bases its behavior based on a property of the variable that invokes it.  Your function might, for example, use the value of a [[User-Defined Attribute]] (UDA) of the calling variable.
+
[[Caution -- no auto-updating from text attributes]]: If you pass the identifier of  a variable <code>V</code> as text to this function, as in
 +
:<code>Variable X := HandleFromIdentifier("V")</code>
 +
the value of <code>X</code>  will be a handle to <code>V</code>, but if the value of <code>V</code> changes it will not automatically recompute <code>X</code> and any of its dependents that may depend on the value of <code>V</code>.  Or if you change the identifier of  <code>V</code>, it will create an error when X is evaluated since the identifier <code>"V"</code> is no longer in use. In this case, you can avoid these problems simply by defining
 +
:<code>Variable X := Handle(V)</code>
 +
in which case Analytica's automatic dependency tracking will know to update <code>X</code> if <code>V</code> changesBut, that might not be possible if the parameter is the result of a more complex expression that finds the identifier of <code>V</code>.
  
= Function [[IndexesOf]] =
+
=== Handle to calling object  ===
  
IndexesOf( A : Array )
+
A [[User-Defined Functions|user-defined function]] can obtain a handle to the variable, function, button or other object that called the function, using:
 +
  HandleFromIdentifier("_Caller")
 +
The tag "_Caller" will not conflict with an identifier since identifiers may not normally begin with "_".  This is useful when you want to write a function that bases its behavior based on an Attribute of the variable that invokes it. For example, an error message function can use it to generate a message that identifies the variable, function, or button in which the error occurred without you having to pass the object as an explicit parameter the function.
  
Returns a list of handles, each one serving as a handle to an index of array A.
+
== IndexesOf(A) ==
 +
[[Syntax]]:
 +
:[[IndexesOf]](A : Array)
  
This is similar to IndexNames(A), except that IndexesOf returns the handles of the indexes, while IndexNames returns the identifier of each index as a text value.
+
Returns a list of handles, each one serving as a handle to an index of array «A».
  
It is possible for an array to have more than one local index having the same identifier.  Obviously, we don't recommend, but in the unusual case where this occurs, the handles returned by IndexesOf are unambiguous, where the results of IndexNames are ambiguous.
+
This is similar to [[IndexNames]](A), except that [[IndexesOf]] returns the handles of the indexes, while [[IndexNames]] returns the identifier of each index as a text value.
  
= Local Variables and Handles =
+
It is possible for an array to have more than one local index having the same identifier.  Obviously, we don't recommend, but in the unusual case where this occurs, the handles returned by [[IndexesOf]] are unambiguous, where the results of [[IndexNames]] are ambiguous.
  
 +
== Local Variables and Handles ==
 
The value of a local variable, like a global variable, may be a number, text, or handle or an array or scalar.  When a local variable is set to a single handle (not an array), there are two behaviors that are possible, depending on the ''type'' of local variable.
 
The value of a local variable, like a global variable, may be a number, text, or handle or an array or scalar.  When a local variable is set to a single handle (not an array), there are two behaviors that are possible, depending on the ''type'' of local variable.
  
 
Starting with Analytica 4.2, there are essentially two (pure) types of local variables: [[MetaVar]]s and [[LocalAlias]]es.  These behave quite differently when you assign them to be a single handle.
 
Starting with Analytica 4.2, there are essentially two (pure) types of local variables: [[MetaVar]]s and [[LocalAlias]]es.  These behave quite differently when you assign them to be a single handle.
  
A [[MetaVar]] local contains whatever you assign it.  Whenever you use its identifier in a value context, its identifier acts as the value assigned to it.  So if you use a [[MetaVar]] identifier in a value context, its value is a handle to an object (not the value of the object pointed to).  When you [[Assignment|assign]] to a [[MetaVar]], you are just changing its value -- the object previously pointed to is not impacted.
+
A {{MetaVar}}-declared local contains whatever you assign it.  Whenever you use its identifier in a value context, its identifier acts as the value assigned to it.  So if you use a {{MetaVar}} identifier in a value context, its value is a handle to an object (not the value of the object pointed to).  When you [[Assignment|assign]] to a {{MetaVar}}, you are just changing its value -- the object previously pointed to is not impacted.  
  
 
A [[LocalAlias]] local acts as an alias of another object when it is set to be a handle.  Any use of a [[LocalAlias]]'s identifier in an expression is equivalent to using the identifier of the object pointed to (at least for objects that exist in the global namespace).  If you [[Assignment|assign]] a new value to a [[LocalAlias]]-type local, you change the object pointed to, and the local remains an alias to that same variable (note: you are only allowed to change the value of another object when the assignment is evaluated from a button script).
 
A [[LocalAlias]] local acts as an alias of another object when it is set to be a handle.  Any use of a [[LocalAlias]]'s identifier in an expression is equivalent to using the identifier of the object pointed to (at least for objects that exist in the global namespace).  If you [[Assignment|assign]] a new value to a [[LocalAlias]]-type local, you change the object pointed to, and the local remains an alias to that same variable (note: you are only allowed to change the value of another object when the assignment is evaluated from a button script).
  
When writing [[meta-inference]] algorithms, where your algorithm will be manipulating handles to objects, we recommend that you declare all your local variables using only [[MetaVar..Do]] or [[LocalAlias..Do]] depending on your intention, and avoid the use of [[Var..Do]], or even [[For..Do]].  The recommendation is only for your own conceptual simplicity, since MetaVar and LocalAlias have such clean semantics, it is var less confusing.  Note however that [[MetaVar..Do]] and [[LocalAlias..Do]] are [[What's new in Analytica 4.2?|new to Analytica 4.2]].   
+
When writing [[meta-inference]] algorithms, where your algorithm will be manipulating handles to objects, we recommend that you declare all your local variables using only [[Local..Do]] or [[LocalAlias..Do]] depending on your intention, and avoid the use of [[Var..Do]], or even [[For..Do]].  The recommendation is only for your own conceptual simplicity, since [[Local]] and [[LocalAlias]] have such clean semantics, it is less confusing than using [[Var]] or [[For]].   
  
 
When a local variable that is declared using [[Var..Do]] is assigned a handle, it acts as a hybrid of the [[MetaVar]] and [[LocalAlias]] semantics.  In a value context, its identifier acts as an alias.  However, when you assign to it, the local is changed to the new value without changing the object originally pointed to.
 
When a local variable that is declared using [[Var..Do]] is assigned a handle, it acts as a hybrid of the [[MetaVar]] and [[LocalAlias]] semantics.  In a value context, its identifier acts as an alias.  However, when you assign to it, the local is changed to the new value without changing the object originally pointed to.
  
 
You may find yourself iterating over a list or array of handles, where you intend to operate on each handle in the list.  Rather than use:
 
You may find yourself iterating over a list or array of handles, where you intend to operate on each handle in the list.  Rather than use:
[[For]] v:=ListOfHandle Do «body»
+
:<code>[[For]] v := [[ListOfHandles]] Do body</code>
 +
 
 
it is better to use one of:
 
it is better to use one of:
[[LocalAlias]] y := ListOfHandle Do «body»
+
:<code>[[LocalAlias]] y := «ListOfHandles» Do «body»</code>
[[MetaVar]] z[] := ListOfHandle Do «body»
+
:<code>{{MetaVar}} z[] := «ListOfHandles» Do «body»</code>
In each iteration of «body», y will be an alias of the object pointed to in the first form, and z will contain a handle to the object in the second form.
 
  
= Function [[IsHandle]](x) =
+
In each iteration of «body», «y» will be an alias of the object pointed to in the first form, and «z» will contain a handle to the object in the second form.
  
 +
== IsHandle(x) ==
 
The function [[IsHandle]](X) tests whether the value contained in «X» is a handle.  If the value of «X» is an array, then it tests whether each cell of the array contains a handle, and returns an array of 0s and 1s.   
 
The function [[IsHandle]](X) tests whether the value contained in «X» is a handle.  If the value of «X» is an array, then it tests whether each cell of the array contains a handle, and returns an array of 0s and 1s.   
  
The call [[IsHandle]](X,local:true), where «X» is a local variable, tests whether the local variable contains a handle.  Note that if you don't include the ''local:true'' parameter, you are testing the value contained within the local, rather than the local itself (except in the case of a [[MetaVar |meta-local]], in which case the test is valid even without the optional ''local:true'').  See [[IsHandle]] for details.
+
The call [[IsHandle]](X, local: true), where «X» is a local variable, tests whether the local variable contains a handle.  Note that if you don't include the <code>local: true</code> parameter, you are testing the value contained within the local, rather than the local itself (except in the case of a [[MetaVar |meta-local]], in which case the test is valid even without the optional <code>local: true</code>).  See [[IsHandle]] for details.
  
= Display of Handles in Result Tables =
+
== Display of handles in Result Tables ==
 +
In an index or array, a cell containing a handle shows the ''title'' of the object in blue.
 +
(or its identifier if "''Show by identifier''" is selected.  Press ''Ctrl-Y'' to toggle this preference.)
  
When a handle object is displayed in a result table cell, the ''title'' or ''identifier'' of the object pointed to will be displayed, depending on whether the "''Show by identifier''" preference is onPressing CTRL-Y toggles this preference, and thus toggles the display between title and identifier.
+
Double click on such a cell to displaying a handle to show the object.
 +
By default, it shows the object's node highlighted in its parent Diagram.
 +
You can change this default behavior for displayed handles in the value of a variable by setting its Attribute
 +
[[Att_hyperlinkPref]]If you set [[Att_hyperlinkPref]]for a module, it controls behavior for all variable in that module (and its submodules).  The attribute has three possible values:
 +
* <code>1</code> = Open its object window
 +
* <code>2</code> = Open its parent diagram, with object highlighted
 +
* <code>3</code> = Same as <code>2</code>, except that for a Module, it opens its diagram with nothing selected.
  
Double clicking on a cell displaying a handle hyperlinks to the object pointed to.  In Analytica 4.1, the hyperlink brings up the object window for the object.  In Analytica 4.2, the default behavior jumps to the diagram containing the object, with the object node highlighted.  In Analytica 4.2, you can control where the hyperlink jumps to by setting the preference attribute [[Att_hyperlinkPref]].  The attribute is inherited, so if you set it for a single variable, the setting controls the behavior only from that variable's result table.  If you set it for a module, that defines the default behavior within that module, or if you set it for your top-level model object, it defines the default behavior model-wide.  The attribute has three possible values:
+
==See Also==
* 1 = Jump to object's object window
+
* [[Handles to Objects]]
* 2 = Jump to parent diagram containing object, with object selected (default)
+
* [[IsHandle]]
* 3 = For handles to modules, open the module's diagram with nothing selected.  For non-modules, jump to diagram containing object with object selected.
+
* [[IndexesOf]]
 +
* [[Meta-Inference]]
 +
* [[FindObjects]]

Latest revision as of 02:36, 5 August 2022




Release:

4.6  •  5.0  •  5.1  •  5.2  •  5.3  •  5.4  •  6.0  •  6.1  •  6.2  •  6.3  •  6.4  •  6.5


A handle is a reference or pointer to a variable, index, function, module, or other Analytica object. Using a handle lets you write variables or functions that work with the object itself rather than the value of the object.

About handles (varTerms)

A handle is refers to a variable rather than the value of the variable. It's useful when we want to operate on the variable as an object -- for example, to see or modify one of its attributes.

If you refer to variable X in an expression, say,

Variable X := 100
Variable A := X

The value of A will be equal to the value of X, namely 100. But if you write:

Variable B := Handle(X)

B now refers to variable X, and you can now access the Title, Description, Inputs or Outputs of X. For example,

Identifier OF (B) --> X

Sometimes it's useful to have a list of handles to objects. For example, the Outputs or Inputs attribute of a variable returns a list of handles to variables and other objects.

If you define a variable as a list of variables:

Variable A := [X, Y, Z]

the value of A will be an array containing the results of evaluating the variables X, Y, and Z. The result will have A an oneIndex (the [self Index]), which is a list of handles to X, Y, and Z. It will also have the union of all the indexes of X, Y, and Z.

When displaying the array as a table, the Index A (usually) shows the titles of X, Y, and Z. The titles are blue, indicating that you can click each item to open the Object view for that object. If "Show By Identifier" is on (from the Object menu, or if you toggle it with control-Y), the index shows identifiers rather than titles. Either way, you can double click to open the object view.

Some attributes consist of lists of handles, notably Inputs, Outputs, and Contains (the objects in a module). The attribute Isin is a handle to the Module containing this object. You can use these to write functions to find the ancestors, descendants, parents or contents of an object.

Handle(v)

Returns a handle (pointer or reference) to object «v».

Syntax:

Handle(v: Variable; AsIndex: optional boolean atomic)

If you want to create a user-defined function that returns a Handle, in most cases the return value needs to involve a call to the Handle function. If you simply place the identifier of a local variable (even if it declared as Variable) as the return value, it will be evaluated. For example, consider these two functions:

Function Fu1(X: Variable) := X
Function Fu2(X: Variable) := Handle(X)
Variable Va1 := 5

Suppose you evaluate Fu1(Va1) and Fu2(Va1). Fu1 returns the value of Va1, that is 5. Fu2 returns a handle to the Va1 object. Hence, to return a Handle, a call to Function Handle was necessary.

You can declare a local to be a Handle using, for example,

Local h := Handle(Va1) do ...

where Va1 is the identifier of an object. In this case, «h» holds a handle. It is not an alias to Va1, but it does serve to identify that object. You can have arrays of handles, and you can have many handles to the same object. When you later assign a different handle to h, the object Va1 does not change, but the local h will then hold a different handle.

You can alternatively declare a local as assign it a handle using, for example

LocalAlias a := Handle(Va1) do...

In this case, within the scope of this LocalAlias declaration, «a» then becomes an alias for Va1. In other words, anywhere you might use the identifier Va1 in an expression, you could equivalently use a.

To create an alias to an index, use

LocalAlias I := Handle(In1, AsIndex: True) do ..

The local identifier I will then serve as an alias to the original index In1 inside the scope of this var statement. The AsIndex parameter causes I's index values to be used when I. appears in a value context (this is relevant when the original object is a self-indexed array, having both a value and an index value).

When an index has handles as elements, and you wish to subscript across that index, the recommended syntax is:

A[I = Handle(x)]

In this example, x would be the identifier appearing as one of the elements of I.

ListOfHandles(x1, x2,..,xn)

Syntax:

ListOfHandles(identifiers: Repeated Object)

Returns a list of handles to one or more objects specified by their identifiers. For example:

ListOfHandles('X', 'Y', 'Z')

returns a list of handles to the three variables. You get the same result from:

[Handle(X), Handle(Y), Handle(Z)]

but the first is usually more convenient. There is another distinction. If you assign the first example, using ListOfHandles, to an Index, it will uses actual handles, which show as clickable blue links containing the title of each object in a table. If you assign the second example, using a simple list, to an Index, each element in the index will appear as the expression, Handle(X), etc, which is not usually what you want.

HandleFromIdentifier(x)

Syntax:

HandleFromIdentifier(varName : atomic text)

Returns a handle to a variable or other object with the identifier «varName». If no such object exists, it returns Null.

Caution -- no auto-updating from text attributes: If you pass the identifier of a variable V as text to this function, as in

Variable X := HandleFromIdentifier("V")

the value of X will be a handle to V, but if the value of V changes it will not automatically recompute X and any of its dependents that may depend on the value of V. Or if you change the identifier of V, it will create an error when X is evaluated since the identifier "V" is no longer in use. In this case, you can avoid these problems simply by defining

Variable X := Handle(V)

in which case Analytica's automatic dependency tracking will know to update X if V changes. But, that might not be possible if the parameter is the result of a more complex expression that finds the identifier of V.

Handle to calling object

A user-defined function can obtain a handle to the variable, function, button or other object that called the function, using:

 HandleFromIdentifier("_Caller")

The tag "_Caller" will not conflict with an identifier since identifiers may not normally begin with "_". This is useful when you want to write a function that bases its behavior based on an Attribute of the variable that invokes it. For example, an error message function can use it to generate a message that identifies the variable, function, or button in which the error occurred without you having to pass the object as an explicit parameter the function.

IndexesOf(A)

Syntax:

IndexesOf(A : Array)

Returns a list of handles, each one serving as a handle to an index of array «A».

This is similar to IndexNames(A), except that IndexesOf returns the handles of the indexes, while IndexNames returns the identifier of each index as a text value.

It is possible for an array to have more than one local index having the same identifier. Obviously, we don't recommend, but in the unusual case where this occurs, the handles returned by IndexesOf are unambiguous, where the results of IndexNames are ambiguous.

Local Variables and Handles

The value of a local variable, like a global variable, may be a number, text, or handle or an array or scalar. When a local variable is set to a single handle (not an array), there are two behaviors that are possible, depending on the type of local variable.

Starting with Analytica 4.2, there are essentially two (pure) types of local variables: MetaVars and LocalAliases. These behave quite differently when you assign them to be a single handle.

A Local-declared local contains whatever you assign it. Whenever you use its identifier in a value context, its identifier acts as the value assigned to it. So if you use a Local identifier in a value context, its value is a handle to an object (not the value of the object pointed to). When you assign to a Local, you are just changing its value -- the object previously pointed to is not impacted.

A LocalAlias local acts as an alias of another object when it is set to be a handle. Any use of a LocalAlias's identifier in an expression is equivalent to using the identifier of the object pointed to (at least for objects that exist in the global namespace). If you assign a new value to a LocalAlias-type local, you change the object pointed to, and the local remains an alias to that same variable (note: you are only allowed to change the value of another object when the assignment is evaluated from a button script).

When writing meta-inference algorithms, where your algorithm will be manipulating handles to objects, we recommend that you declare all your local variables using only Local..Do or LocalAlias..Do depending on your intention, and avoid the use of Var..Do, or even For..Do. The recommendation is only for your own conceptual simplicity, since Local and LocalAlias have such clean semantics, it is less confusing than using Var or For.

When a local variable that is declared using Var..Do is assigned a handle, it acts as a hybrid of the MetaVar and LocalAlias semantics. In a value context, its identifier acts as an alias. However, when you assign to it, the local is changed to the new value without changing the object originally pointed to.

You may find yourself iterating over a list or array of handles, where you intend to operate on each handle in the list. Rather than use:

For v := ListOfHandles Do body

it is better to use one of:

LocalAlias y := «ListOfHandles» Do «body»
Local z[] := «ListOfHandles» Do «body»

In each iteration of «body», «y» will be an alias of the object pointed to in the first form, and «z» will contain a handle to the object in the second form.

IsHandle(x)

The function IsHandle(X) tests whether the value contained in «X» is a handle. If the value of «X» is an array, then it tests whether each cell of the array contains a handle, and returns an array of 0s and 1s.

The call IsHandle(X, local: true), where «X» is a local variable, tests whether the local variable contains a handle. Note that if you don't include the local: true parameter, you are testing the value contained within the local, rather than the local itself (except in the case of a meta-local, in which case the test is valid even without the optional local: true). See IsHandle for details.

Display of handles in Result Tables

In an index or array, a cell containing a handle shows the title of the object in blue. (or its identifier if "Show by identifier" is selected. Press Ctrl-Y to toggle this preference.)

Double click on such a cell to displaying a handle to show the object. By default, it shows the object's node highlighted in its parent Diagram. You can change this default behavior for displayed handles in the value of a variable by setting its Attribute Att_hyperlinkPref. If you set Att_hyperlinkPreffor a module, it controls behavior for all variable in that module (and its submodules). The attribute has three possible values:

  • 1 = Open its object window
  • 2 = Open its parent diagram, with object highlighted
  • 3 = Same as 2, except that for a Module, it opens its diagram with nothing selected.

See Also

Comments


You are not allowed to post comments.