Modula-3: Procedure types

# Procedure types

A procedure is either `NIL` or a triple consisting of:

- the body, which is a statement,
- the signature, which specifies the procedure's formal arguments, result type, and raises set (the set of exceptions that the procedure can raise),
- the environment, which is the scope with respect to which variable names in the body will be interpreted.

A procedure that returns a result is called a function procedure; a procedure that does not return a result is called a proper procedure. A top-level procedure is a procedure declared in the outermost scope of a module. Any other procedure is a local procedure. A local procedure can be passed as a parameter but not assigned, since in a stack implementation a local procedure becomes invalid when the frame for the procedure containing it is popped.

A procedure constant is an identifier declared as a procedure. (As opposed to a procedure variable, which is a variable declared with a procedure type.)

A procedure type declaration has the form:

        TYPE T = PROCEDURE sig

where `sig` is a signature specification, which has the form:

        (formal_1; ...; formal_n): R RAISES S

where

- Each `formal_i` is a formal parameter declaration, as described below.
- `R` is the result type, which can be any type but an open array type. The "`: R`" can be omitted, making the signature that of a proper procedure.
- `S` is the raises set, which is either an explicit set of exceptions with the syntax `{E_1, ..., E_n}`, or the symbol `ANY` representing the set of all exceptions. If "`RAISES S`" is omitted, "`RAISES {}`" is assumed.

A formal parameter declaration has the form

        Mode Name: Type := Default

where

- `Mode` is a parameter mode, which can be `VALUE`, `VAR`, or `READONLY`. If `Mode` is omitted, it defaults to `VALUE`.
- `Name` is an identifier that names the parameter. The parameter names must be distinct.
- `Type` is the type of the parameter.
- `Default` is a constant expression, the default value for the parameter. If `Mode` is `VAR`, "`:= Default`" must be omitted, otherwise either "`:= Default`" or "`: Type`" can be omitted, but not both. If `Type` is omitted, it is taken to be the type of `Default`. If both are present, the value of `Default` must be a member of `Type`.

When a series of parameters share the same mode, type, and default, `name_i` can be a list of identifiers separated by commas. Such a list is shorthand for a list in which the mode, type, and default are repeated for each identifier. That is:

        Mode v_1, ..., v_n: Type := Default

is shorthand for:

        Mode v_1: Type := Default; ...; Mode v_n: Type := Default

This shorthand is eliminated from the expanded definition of the type. The default values are included.

A procedure value `P` is a member of the type `T` if it is `NIL` or its signature is covered by the signature of `T`, where `signature_1` covers `signature_2` if:

- They have the same number of parameters, and corresponding parameters have the same type and mode.
- They have the same result type, or neither has a result type.
- The raises set of `signature_1` contains the raises set of `signature_2`.

The parameter names and defaults affect the type of a procedure, but not its value. For example, consider the declarations:

        PROCEDURE P(txt: TEXT := "P") =
          BEGIN
            Wr.PutText(Stdio.stdout, txt)
          END P;

        VAR q: PROCEDURE(txt: TEXT := "Q") := P;

Now `P = q` is `TRUE`, yet `P()` prints "`P`" and `q()` prints "`Q`". The interpretation of defaulted parameters is determined by a procedure's type, not its value; the assignment `q := P` changes `q`'s value, not its type.

Examples of procedure types:

        TYPE
          Integrand = PROCEDURE (x: REAL): REAL;
          Integrator = PROCEDURE(f: Integrand; lo, hi: REAL): REAL;

          TokenIterator = PROCEDURE(VAR t: Token) RAISES {TokenError};

          RenderProc = PROCEDURE(
            scene: REFANY;
            READONLY t: Transform := Identity)

In a procedure type, `RAISES` binds to the closest preceding `PROCEDURE`. That is, the parentheses are required in:

        TYPE T = PROCEDURE (): (PROCEDURE ()) RAISES {}

    Last modified on Wed Apr  3 09:39:00 PST 1996 by heydon
         modified on Mon Dec 19 08:18:44 PST 1994 by kalsow