Using types in programs

Define a type as a synonym for an existing data type, or as a shortcut for records and array structures.

Defining and using a type

After declaring a type, it can be used as a normal data type to define variables.

TYPE t_customer RECORD
         cust_num INTEGER,
         cust_name VARCHAR(50),
         cust_addr VARCHAR(200)
    END RECORD
...
     DEFINE c1 t_customer
...
     DEFINE o1 RECORD
                order_num INTEGER,
                customer t_customer,
                ...
            END RECORD
...
     DEFINE custlist DYNAMIC ARRAY OF t_customer

The scope of a type is the same as for variables and constants. Types can be global, module-specific, or local to a function.

Using a type defined in another module

A good practice is to define types that belong to the same domain in a single .4gl module, and import that module in the modules where the types are needed.

By default, module-specific types are private; They cannot be used by an other module of the program. To make a module type public, add the PUBLIC keyword before TYPE. When a module type is declared as public, it can be referenced by another module by using the IMPORT FGL instruction:

-- customers.4gl
PUBLIC TYPE t_ord RECORD
           ord_id INTEGER,
           ord_date DATE,
           ord_total DECIMAL(10,2)
       END RECORD
PUBLIC TYPE t_cust RECORD
           cust_id INTEGER,
           cust_name VARCHAR(50),
           orders DYNAMIC ARRAY OF t_ord,
           ...
       END RECORD
...

-- main.4gl
IMPORT FGL customers
MAIN
    DEFINE custlist DYNAMIC ARRAY OF t_cust
    ...
END MAIN

Types for function references

Types can also be used to declare a function signature, in order to define program variables that reference functions with that signature:
TYPE callback_function FUNCTION(p1 INT, p2 INT) RETURNS INT
DEFINE v callback_function
    ...
    LET v = FUNCTION add
    ...

Completing types with methods and interfaces

Types define data structures that can be manipulated from methods, to use the concept of encapsulation and make your code more robust:
PUBLIC TYPE Circle RECORD
    pos_x FLOAT,
    pos_y FLOAT,
    diameter FLOAT
END RECORD

PUBLIC FUNCTION (c Circle) area() RETURNS FLOAT
    RETURN util.Math.pi() * (c.diameter / 2) ** 2
END FUNCTION
Furthermore, to manipulate similar but different data structures with the same set of methods, you can define interfaces to indirectly manipulate different instances of types:
PUBLIC TYPE Circle RECORD ... END RECORD
PUBLIC FUNCTION (c Circle) area() RETURNS FLOAT ... END FUNCTION

PUBLIC TYPE Rectangle RECORD ... END RECORD
PUBLIC FUNCTION (r Rectangle) area() RETURNS FLOAT ... END FUNCTION

PUBLIC TYPE Triangle RECORD ... END RECORD
PUBLIC FUNCTION (r Triangle) area() RETURNS FLOAT ... END FUNCTION

PUBLIC TYPE Shape INTERFACE
    area() RETURNS FLOAT
END INTERFACE