Methods
A function declared with a receiver type defines a method for this type.
Purpose of methods
Methods are functions that perform on a variable with a specific user-defined type, and cannot be used for another type.
Use methods to implement the interface (the access methods) for a type.
Note: The concept of methods allows you to write robust code like in Object-Oriented Programming
languages, without the complexity and traps of OOP.
Defining a method
A method is a FUNCTION
defined with a receiver argument specified in parentheses
before the function name.
The receiver consists of an identifier followed by a user-defined type. The receiver identifier
is then referenced in the function body as the target of this
method:
TYPE Rectangle RECORD
height, width FLOAT
END RECORD
FUNCTION (r Rectangle) area() RETURNS FLOAT
RETURN r.height * r.width
END FUNCTION
Method definition rules
The receiver type must be a TYPE
defined in the same module as the method, and
it must define a structured RECORD
type (it cannot be a flat type defined with a
primitive type such as INTEGER
).
Important: Receiver types and methods for this type must be defined in the same module.
If the receiver type is not defined in the same module as the method, the compiler produces the
error -8426.
Method names and receiver type field names must be different: It is not legal to define a method
with the same name as a field of the receiver
type:
PUBLIC TYPE Rectangle RECORD
height, width FLOAT
END RECORD
...
PUBLIC FUNCTION (r Rectangle) width () RETURNS INTEGER
| Method and field names must be different. Type rectangle.Rectangle has field width.
| See error number -8427.
RETURN r.width
END FUNCTION
The compiler is more strict regarding methods definitions, compared to regular functions. For
example, when a method returns values, it must be defined with the
RETURNS
clause:PUBLIC TYPE Rectangle RECORD
height, width FLOAT
END RECORD
...
PUBLIC FUNCTION (r Rectangle) getWidth ()
RETURN r.width
| A method without return type (RETURNS) can not return values.
| See error number -8431.
END FUNCTION
Invoking a method
To invoke a method, prefix the method with the variable of the type used as receiver for the
method:
DEFINE r1, r2 Rectangle
DISPLAY r1.area()
DISPLAY r2.area()
Polymorphism
The same function name can be reused for a different receiver type. Using a different receiver
type for the same function name implies another
method:
FUNCTION (r Rectangle) area() RETURNS FLOAT
RETURN r.height * r.width
END FUNCTION
FUNCTION (r Triangle) area() RETURNS FLOAT
RETURN ( r.height * r.base ) / 2
END FUNCTION
Receiver variable is passed by reference
A method for a type can implicitly modify the receiver (the receiver variable is passed by
reference to the
method):
FUNCTION (r Rectangle) setDimensions(w FLOAT, h FLOAT)
CALL myAssert( (w>0 AND h>0), "Invalid dimensions!" ) -- stops program
LET r.width = w
LET r.height = h
END FUNCTION
Methods with record parameters
When defining a method taking a structured record type as parameter, you must call the method
with a record variable name. The record will be passed as a copy: The original record cannot be
modified, unless the
INOUT
clause is used. Note: The
.*
dot star notation (expending the record fields on the stack) is not
allowed when calling a method with a record as parameter. This technique is supported with regular
functions for backward compatibility.TYPE t_rec1 RECORD
f1 INT
END RECORD
TYPE t_rec2 RECORD
f1 INT
END RECORD
FUNCTION (r t_rec1) method1(p t_rec2)
LET p.f1 = 999
END FUNCTION
FUNCTION (r t_rec1) method2(p t_rec2 INOUT)
LET p.f1 = 999
END FUNCTION
FUNCTION main()
DEFINE r1 t_rec1, r2 t_rec2
CALL r1.method1( r2 )
DISPLAY r2.f1 -- not modified
CALL r1.method2( r2 )
DISPLAY r2.f1 -- 999
-- CALL r1.method1( r2.* ) -- invalid!
END FUNCTION