Using Caché ObjectScript
Callable User-defined Code Modules
[Back] [Next]
   
Server:docs2
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

This chapter describes how to create and invoke user-defined modules of Caché ObjectScript code. These units of code can be user-defined functions, procedures, methods, routines, or subroutines.

As in other languages, ObjectScript allows you to create named code blocks that you can invoke directly. Such blocks are known as procedures. Strictly speaking, in ObjectScript terminology, a code block that is a procedure has a specific syntax and structure.
The syntax of a procedure definition is as follows:
ProcedureName(Parameters) [PublicVariables] 
 {

    /* code goes here */

    QUIT ReturnValue
 }
The elements of the procedure, here called ProcedureName, are:
Note:
For those familiar with versions of Caché before version 5 (and code written for those versions), procedures represent an advance from coding that was previously available with subroutines and user-defined functions. Procedure parameters are automatically local in scope within the procedure. They do not require a NEW command to ensure that they do not overwrite other values, since they are private to the procedure and do not interact with the symbol table. Also, the explicit declaration of public variables allows you to refer to global variables within an application, such a bank-wide interest rate; it also allows you to create and set values for variables within the procedure that are available to the rest of an application.
Procedures are a particular kind of ObjectScript routine.
Caché also provides a large number of system-supplied functions, all of which are described in the Caché ObjectScript Language Reference; these are sometimes known as intrinsic functions. Calls to system functions are identified by a “$” prefix.
Procedures, Routines, Subroutines, Functions, and Methods: What Are They?
This chapter describes how to implement your own code using procedures, which are the recommended form for implementing user-defined functionality. Caché documentation as a whole talks about procedures, routines, subroutines, functions, and methods. Though all these entities share features, each has its own characteristics. Most notably, applications created before the release Caché 5.0 often include subroutines and functions that are not implemented as procedures. For that reason, it is important to provide a brief overview of all of these different structures for implementing your own code.
The most flexible, most powerful, and recommended form of named, user-defined code block is the procedure. The features of a procedure includes that it:
Important:
By default, methods are procedures. Because of this, all content on procedures in this chapter also describes methods. For more information about methods, see the chapter Methods in the book Using Caché Objects.
By contrast:
Note:
ObjectScript also supports a related form of user-defined code through its macro facility.
Routines
A routine is a callable block of user-written code that is a Caché ObjectScript program. A routine performs commonly needed operations. Its name is determined by the name of the .MAC file that you choose for saving it. Depending on if a routine returns a value, you can invoke a routine with one or both of the following sets of syntax:
   DO ^RoutineName
   SET x = $$^RoutineName
A routine is defined within a namespace. You can use an extended routine reference to execute a user-defined routine defined in a namespace other than the current namespace:
  DO ^|"SAMPLES"|RoutineName
Generally, routines serve as containers for subroutines, methods, and procedures.
The routine is identified by a label (also referred to as a tag) at the beginning of the block of code. This label is the name of the routine. This label is (usually) followed by parentheses which contain a list of parameters to be passed from the calling program to the routine.
When you save a routine to a file, the file name cannot include the underscore (“_”), hyphen (“-”), or semicolon (“;”) characters; names that include such characters are not valid.
Subroutines
A subroutine is a named block of code within a routine. Typically, a subroutine begins with label and ends with a QUIT statement It can accept parameters and does not return a value. To invoke a subroutine, use the following syntax:
DO Subroutine^Routine
where Subroutine is a code block within the Routine file (Routine.MAC).
The form of a subroutine is:
Label(parameters) // comment
 // code
 QUIT // note that QUIT has no arguments
For more details on subroutines, see the section below on subroutines as legacy code.
If you enclose the code and QUIT statement within curly braces, the subroutine is a procedure and can be treated as such.
Functions
Caché comes with many system-supplied functions (sometimes known as “intrinsic” functions), which are described in the Caché ObjectScript Language Reference. This section describes user-defined (“extrinsic”) functions.
A function is a named block of code within a routine. Typically, a function begins with label and ends with a QUIT statement. It can accept parameters and can also return a value. To invoke a function, there are two valid forms of the syntax:
SET rval=$$Function()  /* returning a value */  
DO Function^Routine   /* ignoring the return value */ 
where Function is a code block within the Routine file (Routine.MAC). In both syntactic forms you can use an extended routine reference to execute a function located in a different namespace.
The form of a function is:
Label(parameters)
 // code
 QUIT ReturnValue
If you enclose the code and QUIT statement within curly braces, the function is a procedure and can be treated as such. Note that because a procedure is private by default, you may wish to specify the PUBLIC keyword, as follows:
Label(parameters) PUBLIC {
 // code
 QUIT ReturnValue }
The following example defines a simple function (MyFunc) and calls it, passing two parameters and receiving a return value:
Main ;
   TRY {
     KILL x
     SET x=$$MyFunc(7,10)
        WRITE "returned value is ",x,!
        RETURN
   }
   CATCH { WRITE $ZERROR,!
   }
MyFunc(a,b) 
   SET c=a+b
   QUIT c   
 
The code invoking the function can ignore the return value, but a function’s QUIT command must specify a return value. Attempting to exit a function with an argumentless QUIT generates a <COMMAND> error. The <COMMAND> error specifies the location of the call that invoked the function, followed by a message that specifies the offset location of the argumentless QUIT command within the called function. Refer to $ZERROR for further details.
For more details on functions, see the section below on functions as legacy code.
Defining Procedures
As in other languages, a procedure is a series of ObjectScript commands (a section of a larger routine) that accomplishes a specific task. Similar to constructs such as If, the code of a procedure is contained within curly braces.
Procedures allow you to define each variable as either public or private. For example, the following procedure, is called “MyProc”:
MyProc(x,y) [a,b] PUBLIC {
 Write "x + y = ", x + y
}
defines a public procedure named “MyProc” which takes two parameters, x and y. It defines two public variables, a and b. All other variables used in the procedure (in this case, x and y) are private variables.
By default, procedures are private, which means that you can only call them from elsewhere in the same routine. You can also create procedures that are public, using the Public keyword after the procedure name. Public procedures can be called from other routines.
Procedures need not have defined parameters. To create procedures with parameters, place a parenthesized list of variables immediately after the label.
Invoking Procedures
To invoke a procedure, either issue a DO command that specifies the procedure, or call it as a function using the “$$” syntax. You can control whether a procedure can be invoked from any program (public), or only from the program in which it is located (private). If invoked with DO, a procedure does not return a value; if invoked as a function call, a procedure returns a value. The “$$” form provides the most functionality, and is generally the preferred form.
Using the $$ Prefix
You can invoke a user-defined function in any context in which an expression is allowed. A user-defined function call takes the form:
$$name([param[ ,...]])
where:
Using the DO Command
You can invoke a user-defined function using the DO command. (You cannot invoke a system-supplied function using the DO command.) A function invoked using DO does not return a value. That is, the function must generate a return value, but the DO command ignores this return value. This greatly limits the use of DO for invoking user-defined functions.
To invoke a user-defined function using DO, you issue a command in the following syntax:
DO label(param[,...])
The DO command calls the function named label and passes it the parameters (if any) specified by param. Note that the $$ prefix is not used, and that the parameter parentheses are mandatory. The same rules apply for specifying the label and param as when invoking a user-defined function using the $$ prefix.
A function must always return a value. However, when a function is called with DO, this returned value is ignored by the calling program.
Procedure Syntax
Procedure syntax:
 label([param[=default]][,...]) [[pubvar[,...]]] [access] {
   code 
} 
Invoking syntax:
DO label([param[,...]]) 
or
 command $$label([param][,...]) 
where
label The procedure name. A standard label. It must start in column one. The parameter parentheses following the label are mandatory.
param A variable for each parameter expected by the procedure. These expected parameters are known as the formal parameter list. The parameters themselves are optional (there may be none, one, or more than one param) but the parentheses are mandatory. Multiple param values are separated by commas. Parameters may be passed to the formal parameter list by value or by reference. Procedures that are routines do not include type information about their parameters; procedures that are methods do include this information. The maximum number of formal parameters is 255.
default An optional default value for the param preceding it. You can either provide or omit a default value for each parameter. A default value is applied when no actual parameter is provided for that formal parameter, or when an actual parameter is passed by reference and the local variable in question does not have a value. This default value must be a literal: either a number, or a string enclosed in quotation marks. You can specify a null string (“”) as a default value. This differs from specifying no default value, because a null string defines the variable, whereas the variable for a parameter with no specified or default value would remain undefined. If you specify a default value that is not a literal, Caché issues a <PARAMETER> error.
pubvar Public variables. An optional list of public variables used by the procedure and available to other routines and procedures. This is a list of variables both defined within this procedure and available to other routines and defined within another routine and available to this procedure. If specified, pubvar is enclosed in square brackets. If no pubvar is specified, the square brackets may be omitted. Multiple pubvar values are separated by commas. All variables not declared as public variables are private variables. Private variables are available only to the current invocation of the procedure. They are undefined when the procedure is invoked, and destroyed when the procedure is exited with a QUIT. If the procedure calls any code outside of that procedure, the private variables are preserved, but are unavailable until the call returns to the procedure. All % variables are always public, whether or not they are listed here. The list of public variables can include one or more of the param specified for this routine.
access An optional keyword that declares whether the procedure is public or private. There are two available values: PUBLIC, which declares that this procedure can be called from any routine. PRIVATE, which declares that this procedure can only be called from the routine in which it is defined. PRIVATE is the default.
code A block of code, enclosed in curly braces. The opening curly brace ({) must be separated from the characters preceding and following it by at least one space or a line break. The closing curly brace (}) must not be followed by any code on the same line; it can only be followed by blank space or a comment. The closing curly brace can be placed in column one. This block of code is only entered by the label.
You cannot insert a line break between a command and its arguments.
Each procedure is implemented as part of a routine; each routine can contain multiple procedures.
In addition to standard ObjectScript syntax, there are special rules governing routines. A line in a routine can have a label at the beginning (also called a tag), ObjectScript code, and a comment at the end; but all of these elements are optional.
InterSystems recommends that the first line of a routine have a label matching the name of the routine, followed by a tab or space, followed by a short comment explaining the purpose of the routine. If a line has a label, you must separate it from the rest of the line with a tab or a space. This means that as you add lines to your routine using Caché Studio, you either type a label and a tab/space, followed by ObjectScript code, or you skip the label and type a tab or space, followed by ObjectScript. So in either case, every line must have a tab or space before the first command.
To denote a single-line comment use either a double slash (“//”) or a semicolon (“;”). If a comment follows code, there must be a space before the slashes or semicolon; if the line contains only a comment, there must be a tab or space before the slashes or semicolon. By definition, there can be no line break within a single-line comment; for a multiline comment, mark the beginning of the comment with “/*” and the end with “*/”.
Procedure Variables
Variables used within procedures are automatically private to that procedure. Hence, you do not have to declare them as such and they do not require a NEW command. To share some of these variables with procedures that this procedure calls, pass them as parameters to the other procedures.
You can also declare public variables. These are available to all procedures; those that this procedure calls and those that called this procedure. A relatively small number of variables should be defined in this way, to act as environmental variables for an application. To define public variables, list them in square brackets following the procedure name and its parameters.
SAMPLES>WRITE

SAMPLES>DO ^publicvarsexample

setting a
setting b
setting c
The sum is: 6
SAMPLES>WRITE

a=1
b=2
SAMPLES>
The publicvarsexample.mac code:
publicvarsexample
    ; examples of public variables
    ;
    DO proc1()   ; call a procedure
    QUIT    ; end of the main routine
    ;
proc1() [a, b]
    ; a private procedure
    ; "c" and "d" are private variables
    {
    WRITE !, "setting a"  SET a = 1
    WRITE !, "setting b"  SET b = 2
    WRITE !, "setting c"  SET c = 3
    SET d = a + b + c
    WRITE !, "The sum is: ", d
    }
 
Public versus Private Variables
Within a procedure, local variables may be either “public” or “private”. The public list (pubvar) declares which variable references in the procedure are added to the set of public variables; all other variable references in the procedure are to a private set seen only by the current invocation of the procedure.
Private variables are undefined when a procedure is entered, and they are destroyed when a procedure is exited with a QUIT.
When code within a procedure calls any code outside of that procedure, the private variables are restored upon the return to the procedure. The called procedure or routine has access to public variables (as well as its own private ones.) Thus, pubvar specifies both the public variables seen by this procedure and the variables used in this procedure that are capable of being seen by a routine that the procedure calls.
If the public list is empty, then all variables are private. In this case, the square brackets are optional.
Variables whose name starts with the “%” character are typically variables used by the system or for some special purpose. All such % variables are implicitly public. They can be listed in the public list (for documentation purposes) but this is not necessary.
Private Variables versus Variables Created with NEW
Note that private variables are not the same as variables newly created with NEW. If a procedure wants to make a variable directly available to other procedures or subroutines that it calls, then it must be a public variable and it must be listed in the public list. If it is a public variable being introduced by this procedure, then it makes sense to perform a NEW on it. That way it will be automatically destroyed when we QUIT the procedure, and also it protects any previous value that public variable may have had. For example, the code:
MyProc(x,y)[name]{
 NEW name
 SET name="John"
 DO xyz^abc
 QUIT
}
enables procedure “xyz” in routine “abc” to see the value “John” for name, because it is public. Invoking the NEW command for name protects any public variable named “name” that may already have existed when the procedure “MyProc” was called.
The NEW command does not affect private variables; it only works on public variables. Within a procedure, it is illegal to specify NEW x or NEW (x) if x is not listed in the public list and x is not a % variable.
Making Formal List Parameters Public
If a procedure has a formal list parameter, (such as “x” or “y” in MyProc(x,y) ) that is needed by other procedures it calls, then the parameter should be listed in the public list.
Thus,
MyProc(x,y)[x] {
 DO abc^rou
 }
makes the value of x, but not y, available to the routine “abc^rou”.
Public and Private Procedures
A procedure can be “public” or “private”. A private procedure can only be called from within the routine in which the procedure is defined, whereas a public procedure can be called from any routine. If the PUBLIC and PRIVATE keywords are omitted, the default is “private”.
For instance,
MyProc(x,y) PUBLIC { }
defines a public procedure, while
MyProc(x,y) PRIVATE { }
and
MyProc(x,y) { }
both define a private procedure.
Parameter Passing
An important features of procedures is their support for parameter passing. This is the mechanism by which you can pass values (or variables) to a procedure as parameters. Of course, parameter passing is not required; for example, procedures with no parameter passing could be used to generate a random number or to return the system date in a format other than the default format. Commonly, however, procedures do use parameter passing.
To set up parameter passing, specify:
When Caché executes a user-defined procedure, it maps the parameters in the actual list, by position, to the corresponding parameters in the formal list. Thus, the value of the first parameter in the actual list is placed in the first variable in the formal list; the second value is placed in the second variable; and so on. The matching of these parameters is done by position, not name. Thus, the variables used for the actual parameters and the formal parameters are not required to have (and usually should not have) the same names. The procedure accesses the passed values by referencing the appropriate variables in its formal list.
The actual parameter list and the formal parameter list may differ in the number of parameters:
If there are more variables in the formal list than there are parameters in the actual list, and a default value is not provided for each, the extra variables are left undefined. Your procedure code should include appropriate IF tests to make sure that each procedure reference provides usable values. To simplify matching the number of actual parameters and formal parameters, you can specify a variable number of parameters.
The maximum number of actual parameters is 254.
When passing parameters to a user-defined procedure, you can use passing by value or passing by reference. You can mix passing by value and passing by reference within the same procedure call.
Passing By Value
To pass by value, specify a literal value, an expression, or a local variable (subscripted or unsubscripted) in the actual parameter list. In the case of an expression, Caché first evaluates the expression and then passes the resulting value. In the case of a local variable, Caché passes the variable’s current value. Note that all specified variable names must exist and must have a value.
The procedure’s formal parameter list contains unsubscripted local variable names. It cannot specify an explicit subscripted variable. However, specifying a variable number of parameters implicitly creates subscripted variables.
Caché implicitly creates and declares any non-public variables used within a procedure, so that already-existing variables with the same name in calling code are not overwritten. It places the existing values for these variables (if any) on the program stack. When it invokes the QUIT command, Caché executes an implicit KILL command for each of the formal variables and restores their previous values from the stack.
In the following example, the SET commands use three different forms to pass the same value to the referenced Cube procedure.
 DO Start()
 WRITE "all done"
Start() PUBLIC {
 SET var1=6
 SET a=$$Cube(6)
 SET b=$$Cube(2*3)
 SET c=$$Cube(var1)
 WRITE !,"a: ",a," b: ",b," c: ",c,!
 QUIT 1
 }
Cube(num) PUBLIC {
 SET result = num*num*num
 QUIT result
}
 
Passing By Reference
To pass by reference, specify a local variable name or the name of an unsubscripted array in the actual parameter list, using the form:
.name
With passing by reference, a specified variable or array name does not have to exist before the procedure reference. Passing by reference is the only way you can pass an array name to a procedure. Note that you cannot pass a subscripted variable by reference.
In passing by reference, each variable or array name in the actual list is bound to the corresponding variable name in the function’s formal list. Passing by reference provides an effective mechanism for two-way communication between the referencing routine and the function. Any change that the function makes to a variable in its formal list is also made to the corresponding by-reference variable in the actual list. This also applies to the KILL command. If a by-reference variable in the formal list is killed by the function, the corresponding variable in the actual list is also killed.
If a variable or array name specified in the actual list does not already exist, the function reference treats it as undefined. If the function assigns a value to the corresponding variable in the formal list, the actual variable or array is also defined with this value.
The following example compares passing by reference with passing by value. The variable a is passed by value, the variable b is passed by reference:
Main
   SET a="6",b="7"
     WRITE "Initial values:",!
     WRITE "a=",a," b=",b,!
   DO DoubleNums(a,.b)
     WRITE "Returned to Main:",!
     WRITE "a=",a," b=",b
DoubleNums(foo,&bar) {
  WRITE "Doubled Numbers:",!
  SET foo=foo*2
  SET bar=bar*2
  WRITE "foo=",foo," bar=",bar,!
  QUIT
  }
 
The following example uses passing by reference to achieve two-way communication between the referencing routine and the function through the variable result. The period prefix specifies that result is passed by reference. When the function is executed, result in the actual parameter list is created and bound to z in the function’s formal parameter list. The calculated value is assigned to z and passed back to the referencing routine in result. The & prefix to z in the formal parameter list is optional and non-functional, but helps to clarify the source code. Note that num and powr are passed by value, not reference. This is an example of mixing passing by value and passing by reference:
Start ; Raise an integer to a power.
 READ !,"Integer= ",num  QUIT:num="" 
 READ !,"Power= ",powr   QUIT:powr=""
 SET output=$$Expo(num,powr,.result)
 WRITE !,"Result= ",output
 GOTO Start
Expo(x,y,&z)
 SET z=x
 FOR i=1:1:y {SET z=z*x}
 QUIT z
Variable Number of Parameters
A procedure can specify that it accepts a variable number of parameters. You do this by appending three dots to the name of the final parameter; for example, vals.... This parameter must be the final parameter in the parameter list. It can be the only parameter in the parameter list. This ... syntax can pass multiple parameters, a single parameter, or zero parameters.
Spaces and new lines are permitted between parameters in the list, as well as before the first parameter and after the final parameter in the list. Whitespace is not permitted between the three dots.
To use this syntax, specify a signature where the name of the final parameter is followed by .... The multiple parameters passed to the method through this mechanism can have values from data types, be object-valued, or be a mix of the two. The parameter that specifies the use of a variable number of parameters can have any valid identifier name.
Caché ObjectScript handles passing a variable number of parameters by creating a subscripted variable, creating one subscript for each passed variable. The top level of the variable contains the number of parameters passed. The variable subscripts contain the passed values.
This is shown in the following example. It uses invals... as the only parameter in the formal parameter list. ListGroceries(invals...) receives a variable number of values passed by value:
Main
   SET a="apple",b="banana",c="carrot",d="dill",e="endive"
   DO ListGroceries(a,b,c,d,e)
   WRITE !,"all done"
ListGroceries(invals...) {
  WRITE invals," parameters passed",!
   FOR i=1:1:invals {
      WRITE invals(i),! }
   }
 
The following example uses morenums... as the final parameter, following two defined parameters. This procedure must receive at least two parameter values, but can receive a variable number of additional parameters:
Main
   SET a=7,b=8,c=9,d=100,e=2000
   DO AddNumbers(a,b,c,d,e)
   WRITE "all done"
AddNumbers(x,y,morenums...) {
  SET sum = x+y
  FOR i=1:1:$GET(morenums, 0) {
     SET sum = sum + $GET(morenums(i)) }
  WRITE "The sum is ",sum,!
  QUIT
  }
 
The following example uses morenums... as the final parameter, following two defined parameters. This procedure receives exactly two parameter values; the morenums... variable number of additional parameters is 0:
Main
   SET a=7,b=8,c=9,d=100,e=2000
   DO AddNumbers(a,b)
   WRITE "all done"
AddNumbers(x,y,morenums...) {
  SET sum = x+y
  FOR i=1:1:$GET(morenums, 0) {
     SET sum = sum + $GET(morenums(i)) }
  WRITE "The sum is ",sum,!
  QUIT
  }
 
As specified, AddNumbers(x,y,morenums...) can receive a minimum of two parameters and a maximum of 255. If you supply defaults for the defined parameters AddNumbers(x=0,y=0,morenums...) this procedure can receive a minimum of no parameters and a maximum of 255.
The following example uses nums... as the only parameter. It receives a variable number of values passed by reference:
Main
   SET a=7,b=8,c=9,d=100,e=2000
   DO AddNumbers(.a,.b,.c,.d,.e)
   WRITE "all done"
AddNumbers(&nums...) {
  SET sum = 0
  FOR i=1:1:$GET(nums, 0) {
     SET sum = sum + $GET(nums(i)) }
  WRITE "The sum is ",sum,!
  QUIT sum
  }
 
When a variable parameter list params... receives parameters passed by reference and passes the params... to a routine, the intermediate routine can add additional parameters (additional nodes in the params array) that will also be passed by reference. This is shown in the following example:
Main
   SET a(1)=10,a(2)=20,a(3)=30
   DO MoreNumbers(.a)
   WRITE !,"all done"
MoreNumbers(&params...) {
   SET params(1,6)=60
   SET params(1,8)=80
   DO ShowNumbers(.params) }
ShowNumbers(&tens...) {
   SET key=$ORDER(tens(1,1,""),1,targ)
   WHILE key'="" {
     WRITE key," = ",targ,!
     SET key=$ORDER(tens(1,1,key),1,targ)
   }
  }
 
The following example shows that this args... syntax can be used in both the formal parameter list and in the actual parameter list. In this example, a variable number of parameters (invals...) are passed by value to ListNums, which doubles their values then passes them as invals... to ListDoubleNums:
Main
   SET a="1",b="2",c="3",d="4"
   DO ListNums(a,b,c,d)
   WRITE !,"back to Main, all done"
ListNums(invals...) {
  FOR i=1:1:invals {
    WRITE invals(i),!
    SET invals(i)=invals(i)*2 }
  DO ListDoubleNums(invals...)
  WRITE "back to ListNums",!
ListDoubleNums(twicevals...)
  WRITE "Doubled Numbers:",!
  FOR i=1:1:twicevals {
    WRITE twicevals(i),! }
  QUIT
  }
 
For specifics about methods that can accept a variable number of parameters, see the section Variable Numbers of Arguments in Methods in the “Methods” chapter of Using Caché Objects.
Procedure Code
The body of code between the braces is the procedure code, and it differs from traditional ObjectScript code in the following ways:
Indirection, XECUTE Commands, and JOB Commands within Procedures
Name indirection, argument indirection, and XECUTE commands that appear within a procedure are not executed within the scope of the procedure. Thus, XECUTE acts like an implied DO of a subroutine that is outside of the procedure.
Indirection and XECUTE only access public variables. As a result, if indirection or an XECUTE references a variable x, then it references the public variable x regardless of whether or not there is also a private x in the procedure. For example:
 SET x="set a=3" XECUTE x ; sets the public variable a to 3
 SET x="label1" DO @x ; accesses the public subroutine label1
Similarly, a reference to a label within indirection or an XECUTE is to a label outside of the procedure. Hence GOTO @A is not supported within a procedure, since a GOTO from within a procedure must be to a label within the procedure.
Other parts of the documentation contain more detail on indirection and the XECUTE command.
Similarly, when you issue a JOB command within a procedure, it starts a child process that is outside the method. This means that for code such as the following:
    KILL ^MyVar
    JOB MyLabel
    QUIT $$$OK
MyLabel
    SET ^MyVar=1
    QUIT
In order for the child process to be able to see the label, the method or the class cannot be contained in a procedure block.
Error Traps within Procedures
If an error trap gets set from within a procedure, it needs to be directly to a private label in the procedure. (This is unlike in legacy code, where it can contain “+offset” or a routine name. This rule is consistent with the idea that executing an error trap essentially means unwinding the stack back to the error trap and then executing a GOTO.)
If an error occurs inside a procedure, $ZERROR gets set to the procedure “ label+offset”, not to a private “label+offset”.
To set an error trap, the normal $ZTRAP or $ETRAP is used, but the value must be a literal. For instance:
 SET $ZTRAP = "abc"
 // sets the error trap to the private label "abc" within this block
For more information on error traps, see the chapter of this document on Error Processing.
Legacy User-Defined Code
Before the addition of procedures to Caché, there was support for user-defined code in the form of subroutines and functions (which themselves can now be implemented as procedures). These legacy entities are described here, primarily to help explicate already-written code; their ongoing use is not recommended.
Subroutines
Syntax
Routine syntax:
label [ ( param [ = default  ][ , ...] ) ] 
 /* code */
 QUIT 
Invoking syntax:
DO label [ ( param [ , ...]  ) ] 
or
GOTO label 
label The name of the subroutine. A standard label. It must start in column one. The parameter parentheses following the label are optional. If specified, the subroutine cannot be invoked using a GOTO call. Parameter parentheses prevent code execution from “falling through” into a subroutine from the execution of the code that immediately precedes it. When Caché encounters a label with parameter parentheses (even if they are empty) it performs an implicit QUIT, ending execution rather than “falling through.”
param The parameter value(s) passed from the calling program to the subroutine. A subroutine invoked using the GOTO command cannot have param values, and must not have parameter parentheses. A subroutine invoked using the DO command may or may not have param values. If there are no param values, empty parameter parentheses may be specified or omitted. Specify a param variable for each parameter expected by the subroutine. The expected parameters are known as the formal parameter list.. There may be none, one, or more than one param. Multiple param values are separated by commas. Caché automatically invokes NEW on the referenced param variables. Parameters may be passed to the formal parameter list by value or by reference.
default An optional default value for the param preceding it. You can either provide or omit a default value for each parameter. A default value is applied when no actual parameter is provided for that formal parameter, or when an actual parameter is passed by reference and the local variable in question does not have a value. This default value must be a literal: either a number, or a string enclosed in quotation marks. You can specify a null string (“”) as a default value. This differs from specifying no default value, because a null string defines the variable, whereas the variable for a parameter with no specified or default value would remain undefined. If you specify a default value that is not a literal, Caché issues a <PARAMETER> error.
code A block of code. This block of code is normally accessed by invoking the label. However, it can also be entered (or reentered) by calling another label within the code block or issuing a label + offset GOTO command. A block of code can contain nested calls to other subroutines, functions, or procedures. It is recommended that such nested calls be performed using DO commands or function calls, rather than a linked series of GOTO commands. This block of code is normally exited by an explicit QUIT command; this QUIT command is not always required, but is a recommended coding practice. You can also exit a subroutine by using a GOTO to an external label.
Description
A subroutine is a block of code identified by a label found in the first column position of the first line of the subroutine. Execution of a subroutine most commonly completes by encountering an explicit QUIT statement.
A subroutine is invoked by either the DO command or the GOTO command.
You can pass parameters to a subroutine invoked by the DO command; you cannot pass parameters to a subroutine invoked by the GOTO command. You can pass parameters by value or by reference. See Parameter Passing.
The same variables are available to a subroutine and its calling routine.
A subroutine does not return a value.
Functions
As of Caché 5, a function, by default and recommendation, is a procedure. You can, however, continue to define a function that is not a procedure. This section describes such functions.
Syntax
Non-procedure function syntax:
label([param[=default]][,...]) 
   code 
   QUIT expression 
Invoking syntax:
command $$label([param[ ,...]]) 
or
DO label([param[ ,...]]) 
label The name of the function. A standard label. It must start in column one. The parameter parentheses following the label are mandatory.
param A variable for each parameter expected by the function. The expected parameters are known as the formal parameter list . There may be none, one, or more than one param. Multiple param values are separated by commas. Caché automatically invokes NEW for the referenced param variables. Parameters may be passed to the formal parameter list by value or by reference.
default An optional default value for the param preceding it. You can either provide or omit a default value for each parameter. A default value is applied when no actual parameter is provided for that formal parameter, or when an actual parameter is passed by reference and the local variable in question does not have a value. This default value must be a literal: either a number, or a string enclosed in quotation marks. You can specify a null string (“”) as a default value. This differs from specifying no default value, because a null string defines the variable, whereas the variable for a parameter with no specified or default value would remain undefined. If you specify a default value that is not a literal, Caché issues a <PARAMETER> error.
code A block of code. This block of code can contain nested calls to other functions, subroutines, or procedures. Such nested calls must be performed using DO commands or function calls. You cannot exit a function’s code block by using a GOTO command. This block of code can only be exited by an explicit QUIT command with an expression.
expression The function’s return value, specified using any valid Caché ObjectScript expression. The QUIT command with expression is a mandatory part of a user-defined function. The value that results from expression is returned to the point of invocation as the result of the function.
Description
User-defined functions are described in this section. Calls to user-defined functions are identified by a “$$” prefix. (A user-defined function is also known as an extrinsic function.)
User-defined functions allow you to add functions to those supplied by Caché. Typically, you use a function to implement a generalized operation that can be invoked from any number of programs.
A function is always called from within a Caché command. It is evaluated as an expression and returns a single value to the invoking command. For example:
 SET x=$$myfunc()
Function Parameters
As a rule, user-defined functions use parameter passing. A function, however, can work without externally supplied values. For example, you can define a function to generate a random number or to return the system date in a format other than the default format. Note that in these cases, too, you must supply the parameter parentheses in both the function definition and the function call, even though the parameter list is empty.
Parameter passing requires:
When Caché executes a user-defined function, it maps the parameters in the actual list, by position, to the corresponding parameters in the formal list. For example, the value of the first parameter in the actual list is placed in the first variable in the formal list; the second value is placed in the second variable; and so on. The matching of these parameters is done by position, not name. Thus, the variables used for the actual parameters and the formal parameters are not required to have (and usually should not have) the same names. The function accesses the passed values by referencing the appropriate variables in its formal list.
If there are more variables in the formal list than there are parameters in the actual list, and a default value is not provided for each, the extra variables are left undefined. Your function code should include appropriate If tests to make sure that each function reference provides usable values.
When passing parameters to a user-defined function, you can use passing by value or passing by reference. You can mix passing by value and passing by reference within the same function call. See Parameter Passing.
Return Value
The basic form for defining a user-defined function is as follows:
label ( parameters )  
 /* code */
 QUIT expression 
The function must contain a QUIT command followed by an expression. Caché terminates the execution of the function when it encounters the QUIT, and returns the single value that results from the associated expression to the invoking program.
If you specify a QUIT command without an expression, Caché issues an error.
Variables
The invoking program and the called function use the same set of variables, with the following special considerations.
Location of Functions
You can define a user-defined function within the routine that references it, or in a separate routine where multiple programs can reference it. Recommended practice is to use one routine to contain all your user-defined function definitions. In this way, you can easily locate any function definition to examine or update it.
Invoking a User-defined Function
You can invoke a user-defined function using either the $$ prefix, or by using the DO command. The $$ form provides the most functionality, and is generally the preferred form.
Using the $$ Prefix
You can invoke a user-defined function in any context in which an expression is allowed. A user-defined function call takes the form:
$$name([param [,...]])
where
Using the DO Command
You can invoke a user-defined function using the DO command. (You cannot invoke a system-supplied function using the DO command.) A function invoked using DO does not return a value. That is, the function must generate a return value, but the DO command ignores this return value. This greatly limits the use of DO for invoking user-defined functions.
To invoke a user-defined function using DO, you issue a command in the following syntax:
DO label(param[ ,...]) 
The DO command calls the function named label and passes it the parameters (if any) specified by param. Note that the $$ prefix is not used, and that the parameter parentheses are mandatory. The same rules apply for specifying the label and param as when invoking a user-defined function using the $$ prefix.
A function must always return a value. However, when a function is called with DO, this returned value is ignored by the calling program.