Indirection (@)
Enables you to assign values indirectly to variables.
Introduction to Indirection
The ObjectScript indirection operator (@) allows you to assign values indirectly to variables. Indirection is a technique that provides dynamic runtime substitution of part or all of a command line, a command, or a command argument by the contents of a data field. InterSystems IRIS® performs the substitution before execution of the associated command.
Although indirection can promote more economical and more generalized coding than would be otherwise available, it is never essential. You can always duplicate the effect of indirection by other means, such as by using the XECUTE command.
You should use indirection only in those cases where it offers a clear advantage. Indirection can have an impact on performance because InterSystems IRIS performs the required evaluation at runtime, rather than during the compile phase. Also, if you use complicated indirections, be sure to document your code clearly. Indirections can sometimes be difficult to decipher.
Indirection is specified by the indirection operator (@) and, except for subscript indirection, takes the form:
@variable
where variable identifies the variable from which the substitution value is to be taken. All variables referenced in the substitution value are public variables, even when used in a procedure. The variable can be an array node.
The following routine illustrates that indirection looks at the entire variable value to its right.
IndirectionExample
SET x = "ProcA"
SET x(3) = "ProcB"
; The next line will do ProcB, NOT ProcA(3)
DO @x(3)
QUIT
ProcA(var)
WRITE !,"At ProcA"
QUIT
ProcB(var)
WRITE !,"At ProcB"
QUIT
InterSystems IRIS recognizes five types of indirection, described on this page; the type of indirection is performed depends on the context in which the @variable occurs.
Indirection cannot be used with dot syntax. This is because dot syntax is parsed at compile time, not at runtime.
Name Indirection
In name indirection, the indirection evaluates to a variable name, a line label, or a routine name. InterSystems IRIS substitutes the contents of variable for the expected name before executing the command.
Name indirection can only access public variables. For further details, see User-defined Code.
When you use indirection to reference a named variable, the value of the indirection must be a complete global or local variable name, including any necessary subscripts. In the following example, InterSystems IRIS sets the variable B to the value of 6.
SET Y = "B",@Y = 6
Important:
If a call attempts to use indirection to get or set the value of object properties, it may result in an error. Do not use calls of this kind, as they attempt to bypass property accessor methods (<PropertyName>Get and <PropertyName>Set). Instead, use the $CLASSMETHOD, $METHOD, and $PROPERTY functions, which are designed for this purpose; see Dynamically Accessing Objects.
When you use indirection to reference a line label, the value of the indirection must be a syntactically valid line label. In the following example, InterSystems IRIS sets D to:
-
The value of the line label FIG if the value of N is 1.
-
The value of the line label GO if the value of N is 2.
-
The value of STOP in all other cases.
Later, InterSystems IRIS passes control to the label whose value was given to D.
B SET D = $SELECT(N = 1:"FIG",N = 2:"GO",1:"STOP")
; ...
LV GOTO @D
When you use indirection to reference a routine name, the value of the indirection must be a syntactically valid routine name. In the following example, name indirection is used on the DO command to supply the appropriate procedure name. At execution time, the contents of variable loc are substituted for the expected name:
Start
READ !,"Enter choice (1, 2, or 3): ",num
SET loc = "Choice"_num
DO @loc
RETURN
Choice1()
; ...
Choice2()
; ...
Choice3()
; ...
Name indirection can substitute only a name value. The second SET command in the following example returns an error message because of the context. When evaluating the expression to the right of the equal sign, InterSystems IRIS interprets @var1 as an indirect reference to a variable name, not a numeric value.
SET var1 = "5"
SET x = @var1*6
You can recast the example to execute correctly as follows:
SET var1 = "var2",var2 = 5
SET x = @var1*6
Pattern Indirection
Pattern indirection is a special form of indirection. The indirection operator replaces a pattern match. The value of the indirection must be a valid pattern. (See Pattern Match Operator.) Pattern indirection is especially useful when you want to select several possible patterns and then use them as a single pattern.
In the following example, indirection is used with pattern matching to check for a valid U.S. Postal (ZIP) code. Such codes can take either a five-digit (nnnnn) or a nine-digit (nnnnnnnnn) form.
The first SET command sets the pattern for the five-digit form. The second SET command sets the pattern for the nine-digit form. The second SET command is executed only if the postconditional expression ($LENGTH(zip) = 10) evaluates to TRUE (nonzero), which occurs only if the user inputs the nine digit form.
GetZip()
SET pat = "5N"
READ !,"Enter your ZIP code (5 or 9 digits): ",zip
SET:($LENGTH(zip)=10) pat = "5N1""-""4N"
IF zip'?@pat {
WRITE !,"Invalid ZIP code"
DO GetZip()
}
The use of indirection with pattern matching is a convenient way to localize the patterns used in an application. In this case, you could store the patterns in separate variables and then reference them with indirection during the actual pattern tests. (This is also an example of name indirection.) To port such an application, you would have to modify only the pattern variables themselves.
Argument Indirection
In argument indirection, the indirection evaluates to one or more command arguments. By contrast, name indirection applies only to part of an argument.
To illustrate this difference, compare the following example with the example given under Name Indirection.
Start
SET rout = "^Test1"
READ !,"Enter choice (1, 2, or 3): ",num
SET loc = "Choice"_num_rout
DO @loc
QUIT
In this case, @loc is an example of argument indirection because it supplies the complete form of the argument (that is, label^routine). In the name indirection example, @loc is an example of name indirection because it supplies only part of the argument (the label name, whose entry point is assumed to be in the current, rather than a separate, routine).
In the following example, the second SET command is an example of name indirection (only part of the argument, the name of the variable), while the third SET command is an example of argument indirection (the entire argument).
SET a = "var1",b = "var2 = 3*4"
SET @a = 5*6
SET @b
WRITE "a = ",a,!
WRITE "b = ",b,!
Subscript Indirection
Subscript indirection is an extended form of name indirection. In subscript indirection, the value of the indirection must be the name of a local or global array node. Subscript indirection is syntactically different than the other forms of indirection. Subscript indirection uses two indirection operators in the following format:
@array@(subscript)
Assume that you have a global array called ^client in which the first-level node contains the client’s name, the second-level node contains the client’s street address, and the third-level node contains the client’s city, state, and ZIP code. To write out the three nodes for the first record in the array, you can use the following form of the WRITE command:
WRITE !,^client(1),!,^client(1,1),!,^client(1,1,1)
When executed, this command might produce output similar to following:
John Jones
42 Arnold St.
Boston, MA 02745
To write out a range of records (say, the first 10), you could modify the code so that the WRITE is executed within a FOR loop. For example:
FOR i = 1:1:10 {
WRITE !,^client(i),!,^client(i,1),!,^client(i,1,1)
}
As the FOR loop executes, the variable i is incremented by 1 and used to select the next record to be output.
While more generalized than the previous example, this is still very specialized code because it explicitly specifies both the array name and the number of records to output.
To transform this code into a more generalized form that would allow a user to list a range of records from any array (global or local) that stores name, street, and city information in three node levels, you could use subscript indirection as shown in the following example:
Start
READ !,"Output Name, Street, and City info.",!
READ !,"Name of array to access: ",name
READ !,"Global or local (G or L): ",gl
READ !,"Start with record number: ",start
READ !,"End with record number: ",end
IF (gl["L")!(gl["l") {SET array = name}
ELSEIF (gl["G")!(gl["g") {SET array = "^"_name}
SET x = 1,y = 1
FOR i = start:1:end {DO Output}
RETURN
Output()
WRITE !,@array@(i)
WRITE !,@array@(i,x)
WRITE !,@array@(i,x,y)
QUIT
The WRITE commands in the Output subroutine use subscript indirection to reference the requested array and the requested range of records.
In the evaluation of subscript indirection, if the instance of indirection refers to an unsubscripted global or local variable, the value of the indirection is the variable name and all characters to the right of the second Indirection operator, including the parentheses.
For a local variable, the maximum number of subscript levels is 255. Subscript indirection cannot reference more than 254 subscripts for a multidimensional object property. For a global variable, the maximum number of subscript levels depends on the subscript, and may be higher than 255, as described in Formal Rules about Globals. Attempting to use indirection to populate a local variable with more than 255 subscript levels results in a <SYNTAX> error.
A class parameter can be used as the base for subscript indirection in the same way that a local or global variable can be used as the base. For example, you can perform subscript indirection using a class parameter with the following syntax:
SET @..#myparam@(x,y) = "stringval"
$TEXT Argument Indirection
As its name implies, $TEXT argument indirection is allowed only in the context of a $TEXT function argument. The value of the indirection must be a valid $TEXT argument.
You use $TEXT argument indirection primarily as a convenience to avoid multiple forms of indirection that produce the same result. For example, if the local variable LINE contains the entry reference " START^MENU", you can use name indirection to the line label and to the routine name to obtain the text for the line, as follows:
SET LINETEXT = $TEXT(@$PIECE(LINE,"^",1)^@$PIECE(LINE,"^",2))
You can use $TEXT argument indirection to produce the same result in a simpler manner, as follows:
SET LINETEXT = $TEXT(@LINE)