Skip to main content

Defining and Calling Methods

This topic describes the rules and options for creating methods in InterSystems IRIS® data platform classes and for calling those methods.

Instance methods apply only to object classes and are not discussed here.

Introduction to Methods

A method is an executable element defined by a class. InterSystems IRIS supports two types of methods: instance methods and class methods. An instance method is invoked from a specific instance of a class and typically performs some action related to that instance. A class method is a method that can be invoked without reference to any object instance; these are called static methods in other languages.

The term method usually refers to an instance method. The more specific phrase class method is used to refer to class methods.

Because you cannot execute an instance method without an instance of an object, instance methods are useful only when defined in object classes. In contrast, you can define class methods in any kind of class.

Defining Methods

To add a class method to a class, add an element like the following to the class definition:

ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}

Where:

  • MethodName is the name of the method. For rules, see Naming Conventions.

  • Arguments is a comma-separated list of arguments. For details, see Specifying Method Arguments.

  • Classname is an optional class name that represents the type of value (if any) returned by this method. Omit the As Classname part if the method does not return a value.

    The class can be a data type class, an object class, or (less commonly) a class of no type. The class name can be a complete class name or a short class name. For details, see Package Use When Referring to Classes.

  • Keywords represents any method keywords. These are optional. See Compiler Keywords.

  • The method implementation depends on the implementation language and type of method; see Specifying the Implementation Language and Types of Methods. By default, the method implementation consists of zero or more lines of ObjectScript.

To add an instance method to a class, use the same syntax with Method instead of ClassMethod:

Method MethodName(arguments) as Classname [ Keywords]
{
 //method implementation
}

Instance methods are relevant only in object classes.

Specifying Method Arguments: Basics

A method can take any number of arguments. The method definition must specify the arguments that it takes. It can also specify the type and default value for each argument. (In this context, type refers to any kind of class, not specifically data type classes.)

Consider the following generic class method definition:

ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
 //method implementation
}

Here Arguments has the following general form:

argname1 as type1 = value1, argname2 as type2 = value2, argname3 as type3 = value3, [and so on]

Where:

  • argname1, argname2, argname3, and so on are the names of the arguments. These names must follow the rules for variable names.

  • type1, type2, type3, and so on are class names. This part of the method definition is intended to describe, to programmers who might use this method, what type of value to pass for the corresponding argument. Generally it is a good idea to explicitly specify the type of each method argument.

    Typically the types are data type classes or object classes.

    The class name can be a complete class name or a short class name. For details, see Package Use When Referring to Classes.

    You can omit this part of the syntax. If you do, also omit the as part.

  • value1, value2, value3, and so on are the default values of the arguments. The method automatically sets the argument equal to this value if the method is called without providing a value for the argument.

    Each value can either be a literal value ("abc" or 42) or an ObjectScript expression enclosed in curly braces. For example:

    ClassMethod Test(flag As %Integer = 0)
    {
     //method implementation
    }

    For another example:

    ClassMethod Test(time As %Integer = {$horolog} )
    {
     //method implementation
    }

    You can omit this part of the syntax. If you do, also omit the equals sign (=).

For instance, here is a Calculate() method with three arguments:

ClassMethod Calculate(count As %Integer, name, state As %String = "CA")
{
 // ...
}

where count and state are declared as %IntegerOpens in a new tab and %StringOpens in a new tab, respectively. By default, arguments are of the %StringOpens in a new tab data type, so that an argument of unspecified type is a %StringOpens in a new tab. This is the case for name in the above example.

Indicating How Arguments Are to Be Passed

The method definition also indicates, to programmers who might use the method, how each argument is expected to be passed. Arguments can be passed by value (the default technique) or by reference. See Passing Arguments to a Method.

It may or may not be sensible to pass a specific argument by reference. The details depend upon the method implementation. Consequently, when you define a method, you should use the method signature to indicate to other programmers how each argument is meant to be used.

To indicate that an argument should be passed by reference, include the ByRef modifier in the method signature, before the name of the argument. The following shows an example that uses ByRef for both its arguments:

/// Swap value of two integers
Method Swap(ByRef x As %Integer, ByRef y As %Integer)
{
    Set temp = x
    Set x = y
    Set y = temp
}

Similarly, to indicate that an argument should be passed by reference and is intended to have no incoming value, include the Output modifier in the method signature, before the name of the argument. For example:

Method CreateObject(Output newobj As MyApp.MyClass) As %Status
{
    Set newobj = ##class(MyApp.MyClass).%New()
    Quit $$$OK
}

Specifying a Variable Number of Arguments

You can define a method that accepts variable numbers of arguments. To do so, include ... after the name of the last argument, as in the following example. This example also shows how this feature can be used.

ClassMethod MultiArg(Arg1... As %String)
{
    Set args = $GET(Arg1, 0)
    Write "Invocation has ",
        args,
        " element",
        $SELECT((args=1):"", 1:"s"), !
    For i = 1 : 1 : args {
        Write "Argument[", i , "]:", ?15, $GET(Arg1(i), "<NULL>"), !
    }
}

The following Terminal session shows how this method behaves:

MYNAMESPACE>do ##class(VarNumArg.Demo).MultiArg("scooby","shaggy","velma","daphne","fred")
Invocation has 5 elements
Argument[1]:   scooby
Argument[2]:   shaggy
Argument[3]:   velma
Argument[4]:   daphne
Argument[5]:   fred

For more details on this ObjectScript feature, see Variable Numbers of Arguments.

Returning a Value

Except in the case of an expression method, to define a method so that it returns a value, use either of the following in the method (if you implement the method in ObjectScript):

 Return returnvalue

Or:

 Quit returnvalue

Where returnvalue is a suitable value for the method to return. This should be consistent with the declared return type of the method. If the return type is a data type class, the method should return a literal value. If the return type is an object class, the method should return an instance of that class (specifically an OREF). For details, see Working with Registered Objects.

For example:

ClassMethod Square(input As %Numeric) As %Numeric
{
    Set returnvalue = input * input
    Return returnvalue
}

For another example, this method returns an object instance:

ClassMethod FindPerson(id As %String) As Person
{
    Set person = ##class(Person).%OpenId(id)
    Return person
}

The syntax for returning a value is different depending on the implementation language of the method.

Restricting Access by Using Privilege Checks

You can restrict access to a method by using the Requires keyword. Only users or processes that have the specified privilege (or privileges) can call the method.

Specify a privilege by naming a resource and the appropriate level of permission (Use, Read, or Write), separated with a colon. Use a comma-delimited list to specify more than one privilege.

In the following example, the MyAction method requires the Use permission on the Service_FileSystem resource:

ClassMethod MyAction() [ Requires = Service_FileSystem: Use ] 
{
  write "You have access to this method."
}

Calling the method without the required privilege results in a <PROTECT> error:

<PROTECT> *Method MyAction' requires resource 'Service_FileSystem: Use' 

If a method inherits the Requires keyword from a superclass, you can add to the list of required privileges by setting a new value for the keyword. You cannot remove required privileges in this manner.

For details, see Privileges and Permissions and see the reference for the Requires keyword.

Specifying the Implementation Language

You have the choice of implementation language when creating a method. In fact, within a single class, it is possible to have multiple methods implemented in different languages. All methods interoperate regardless of implementation language.

By default, a method uses the language specified by the Language keyword of the class to which it belongs. For this keyword, the default is objectscript (ObjectScript); the other options are python, tsql, and ispl. To override the language for a specific method, set the Language keyword for that method:

Method Normalize(addr As %String) [ Language = python ]
{

    from scourgify import normalize_address_record
    normalized = normalize_address_record(addr)

    self.AddressLine1 = normalized['address_line_1']
    self.AddressLine2 = normalized['address_line_2']
    self.City = normalized['city']
    self.State = normalized['state']
    self.PostalCode = normalized['postal_code']
}

For details, see Language (Method Keyword).

Types of Methods (CodeMode Options)

InterSystems IRIS supports four types of methods, which the class compiler handles differently:

Code Methods

A code method is a method whose implementation is simply lines of code. This is the most typical type of method and is the default.

The method implementation can contain any code that is valid for the implementation language.

Note:

InterSystems IRIS comes with a set of system-defined methods that perform simple, common tasks. If a user-defined method performs one of these tasks, then the compiler does not generate any executable code for it. Rather, it associates the user-defined method with the system-defined method, so that invoking the user-defined method results in a call to the system-defined method, with an associated performance benefit. Also, the debugger does not step through such a system-defined method.

Expression Methods

An expression method is a method whose implementation consists of only an expression. When executed, the method returns the value of that expression. Expression methods are typically used for simple methods (such as those found in data type classes) that need rapid execution speed.

For example, it is possible to convert the Speak() method of the Dog class from the previous example into an expression method:

Method Speak() As %String [CodeMode = expression]
{
    "Woof, Woof"
}

Assuming dog refers to a Dog object, this method could be used as follows:

   Write dog.Speak()

Which could result in the following code being generated:

   Write "Woof, Woof"

It is a good idea to give default values to all formal variables of an expression method. This prevents potential substitution problems caused by missing actual variables at runtime.

Note:

InterSystems IRIS does not allow macros or call-by-reference arguments within expression methods.

Call Methods

A call method is a special mechanism to create method wrappers around existing InterSystems IRIS routines. This is typically useful when migrating legacy code to object-based applications.

Defining a method as a call method indicates that a specified routine is called whenever the method is invoked. The syntax for a call method is:

Method Call() [ CodeMode = call ]
{
    Tag^Routine
}

where Tag^Routine specifies a tag name within a routine.

Method Generators

A method generator is actually a small program that is invoked by the class compiler during class compilation. Its output is the actual runtime implementation of the method. Method generators provide a means of inheriting methods that can produce high performance, specialized code that is customized to the needs of the inheriting class or property. Within the InterSystems IRIS library, method generators are used extensively by the data type and storage classes.

For details, see Defining Method and Trigger Generators.

Projecting a Method As an SQL Stored Procedure

You can define a class method (but not an instance method) so that it is also available as an SQL stored procedure. To do so, include the SqlProc keyword in the method definition.

The default name of the procedure is as CLASSNAME_METHODNAME To specify a different name, specify the SqlName keyword.

For details, see Defining Stored Procedures.

Calling Class Methods

This section discusses how to call class methods in ObjectScript. This section applies to all kinds of classes. Note that instance methods, which apply only to object classes, are not discussed here.

  • To call a class method of any class (if that method is not private), use the following expression:

    ##class(Package.Class).Method(Args)
    

    Where Package.Class is the name of the class, Method is the name of the method, and Args is any arguments of the method.

    ##class is not case-sensitive.

    This expression invokes the given class method and obtains its return value (if any). You can use this expression with commands such as DO and SET, or you can use it as part of another expression. The following shows some variations:

     do ##class(Package.Class).Method(Args)
     set myval= ##class(Package.Class).Method(Args)
     write ##class(Package.Class).Method(Args) 
     set newval=##class(Package.Class).Method(Args)_##class(Package2.Class2).Method2(Args)

    You can omit the package. If you do so, the class compiler determines the correct package name to use (this name resolution is explained in Packages).

  • (Within a class method) to call another class method of that class (which could be an inherited method), use the following expression:

    ..MethodName(args)
    

    You can use this expression with the DO command. If the method returns a value, you can use SET, or you can use it as part of another expression. The following shows some variations:

     do ..MethodName()
     set value=..MethodName(args)
    
    Note:

    You cannot use this syntax in a class method to refer to a property or an instance method, because those references require the instance context.

  • To execute a class method, where the method name is not determined until runtime, use the $CLASSMETHOD function:

    $CLASSMETHOD(classname, methodname, Arg1, Arg2, Arg3, ... )
    

    where classname evaluates to the fully qualified name of a class, methodname evaluates to the name of a class method in that class, and Arg1, Arg2, Arg3, and so on are any arguments to that method. For example:

     set cls="Sample.Person"
     set clsmeth="PrintPersons" 
     do $CLASSMETHOD(cls,clsmeth)

    executes the PrintPersons method from the Sample.Person class.

    For more information, see $CLASSMETHOD.

If the given method does not exist or if it is an instance method instead, the system generates the <METHOD DOES NOT EXIST> error. If the given method is private, the system generates the <PRIVATE METHOD> error.

Passing Arguments to a Method

The default way to pass arguments to methods is by value. In this technique, simply include the arguments as variables, literal values, or other expressions within the method call, as shown in the preceding examples.

It is also possible to pass arguments by reference.

This works as follows: the system has a memory location that contains the value of each local variable. The name of the variable acts as the address to the memory location. When you pass a local variable to a method, you pass the variable by value. This means that the system makes a copy of the value, so that the original value is not affected. You can pass the memory address instead; this technique is known as calling by reference. It is also the only way to pass a multidimensional array as an argument.

In ObjectScript, to pass an argument by reference, precede that argument with a period. For example:

 set MyArg(1)="value A"
 set MyArg(2)="value B"
 set status=##class(MyPackage.MyClass).MyMethod(.MyArg)

In this example, we pass a value (a multidimensional array) by reference so that the method could receive the value. In other cases, it is useful to pass an argument by reference so that you can use its value after running the method. For example:

 set status=##class(MyPackage.MyClass).GetList(.list)
 //use the list variable in subsequent logic

In other cases, you might specify a value for the variable, invoke a method that modifies it (and that returns it by reference), and then use the changed value.

Casting a Method

To cast a method of one class as a method of another class, the syntax is either of the following (in ObjectScript):

Do ##class(Package.Class1)Class2Instance.Method(Args)
Set localname = ##class(Package.Class1)Class2Instance.Method(Args)

You can cast both class methods and instance methods.

For example, suppose that two classes, MyClass.Up and MyClass.Down, both have Go() methods. For MyClass.Up, this method is as follows

Method Go()
{
    Write "Go up.",!
}

For MyClass.Down, the Go() method is as follows:

Method Go()
{
    Write "Go down.",!
}

You can then create an instance of MyClass.Up and use it to invoke the MyClass.Down.Go method:

>Set LocalInstance = ##class(MyClass.Up).%New()
 
>Do ##class(MyClass.Down)LocalInstance.Go()
Go down.

It is also valid to use ##class as part of an expression, as in

 Write ##class(Class).Method(args)*2

without setting a variable equal to the return value.

A more generic approach is to use the $METHOD and $CLASSMETHOD functions, which are for instance and class methods, respectively.

Overriding an Inherited Method

A class inherits methods (both class and instance methods) from its superclass or superclasses. Except for methods that are marked Final, you can override these definitions by providing a definition within this class. If you do so, note the following rules:

  • If the method is a class method in the superclass, you cannot override it as an instance method in the subclass, and vice versa.

  • The return type of the subclass method must be either the same as the original return type or a subclass of the original return type.

  • The method in the subclass can have more arguments than the method in the superclass. (Also see Number of Arguments.)

  • The method in the subclass can specify different default values for the arguments.

  • The types of the arguments in the subclass method must be consistent with the types of the arguments in the original method. Specifically, any given argument must be either the same as the original type or a subclass of the original type.

    Note that if an argument has no specified type, the compiler treats the argument as %StringOpens in a new tab. Thus if an argument in the superclass method has no type, the corresponding argument of a subclass method can be %StringOpens in a new tab, can be a subclass of %StringOpens in a new tab, or can have no type.

  • The method in the subclass should receive argument values in the same way as the method in the superclass. For example, if a given argument is passed by reference in the superclass, the same argument should be passed by reference in the subclass.

    If the method signatures are inconsistent in this regard, it is harder for other developers to know how to use the methods appropriately. Note, however, that the compiler does not issue an error.

If your method implementation needs to call the method of the same name as defined in the superclass, you can use the syntax ##super(), which is discussed in the subsections. This discussion applies to code that is written in ObjectScript.

##super()

Within a method, use the following expression to call the method of the same name as defined in the nearest superclass:

##super()

You can use this expression with the DO command. If the method returns a value, you can use SET, or you can use it as part of another expression. The following shows some variations:

 do ##super()
 set returnvalue=##super()_"additional string"
Note:

##super is not case-sensitive.

This is useful if you define a method that should invoke the existing method of the superclass and then perform some additional steps such as modifying its returned value.

##super and Method Arguments

##super also works with methods that accept arguments. If the subclass method does not specify a default value for an argument, make sure that the method passes the argument by reference to the superclass.

For example, suppose the code for the method in the superclass (MyClass.Up.SelfAdd()) is:

ClassMethod SelfAdd(Arg As %Integer)
{
    Write Arg,!
    Write Arg + Arg
}

then its output is:

>Do ##Class(MyClass.Up).SelfAdd(2)
2
4
>

The method in the subclass (MyClass.Down.SelfAdd()) uses ##super and passes the argument by reference:

ClassMethod SelfAdd(Arg1 As %Integer)
{
    Do ##super(.Arg1)
    Write !
    Write Arg1 + Arg1 + Arg1
}

then its output is:

>Do ##Class(MyClass.Down).SelfAdd(2)
2
4
6
>

In MyClass.Down.SelfAdd(), notice the period before the argument name. If we omitted this and we invoked the method without providing an argument, we would receive an <UNDEFINED> error.

Calls That ##super Affects

##super only affects the current method call. If that method makes any other calls, those calls are relative to the current object or class, not the superclass. For example, suppose that MyClass.Up has MyName() and CallMyName() methods:

Class MyClass.Up Extends %Persistent
{

ClassMethod CallMyName()
{
    Do ..MyName()
}

ClassMethod MyName()
{
    Write "Called from MyClass.Up",!
}

}

and that MyClass.Down overrides those methods as follows:

Class MyClass.Down Extends MyClass.Up
{

ClassMethod CallMyName()
{
    Do ##super()
}

ClassMethod MyName()
{
    Write "Called from MyClass.Down",!
}

}

then invoking the CallMyName() methods have the following results:

USER>d ##class(MyClass.Up).CallMyName()
Called from MyClass.Up
 
USER>d ##class(MyClass.Down).CallMyName()
Called from MyClass.Down

MyClass.Down.CallMyName() has different output from MyClass.Up.CallMyName() because its CallMyName() method includes ##super and so calls the MyClass.Up.CallMyName() method, which then calls the uncast MyClass.Down.MyName() method.

Number of Arguments

In some cases, you might find it necessary to add new arguments to a method in a superclass, thus resulting in more arguments than are defined in the method in a subclass. The subclasses will still compile, because (for convenience) the compiler appends the added arguments to the method in the subclass. In most cases, you should still examine all the subclasses that extend the method, and edit the signatures to account for the additional arguments, and decide whether you want to edit the code also. Even if you do not want to edit signatures or code, you still must consider two points:

  • Make sure that the added argument names are not the same as the names of any variables used in the method in the subclass. The compiler appends the added arguments to the method in the subclass. If these arguments happen to have the same names as variables used in the method of the subclass, unintended results will occur.

  • If the method in the subclass uses the added arguments (because this method uses ##super), make sure that the method in the superclass specifies default values for the added arguments.

See Also

FeedbackOpens in a new tab