AN OVERALL PROGRAM OUTLINE
In order to completely define the procedure, we will need to
lay some groundwork in the form of a few definitions.
Program Heading - This is the easiest part since it is only
one line, at least it has been in all of our programs up
to this point. It is simply the MODULE line, and it
doesn't need to be any more involved than it has been up
to this point except for one small addition which we will
cover in a later chapter.
Declaration Part - This is the part of the Modula-2 source
code in which all constants, variables, and other user
defined auxiliary operations are defined. In some of the
programs we have examined, there have been one or more
var declarations and in one case a constant was declared.
These are the only components of the declaration part we
have used up to this time. There are actually four
components in the declaration part, and the procedures
make up the fourth part. We will cover the others in the
Statement Part - This is the last part of any Modula-2
program, and it is what we have been calling the main
program. It always exists bounded by the reserved words
BEGIN and END just as it has in all of our examples to
It is very important that you grasp the above definitions
because we will be referring to them constantly during this
chapter, and throughout the remainder of this tutorial. With
that introduction, let us look at our first Modula-2 program
with a procedure in it. It will, in fact, have three
WHAT IS A PROCEDURE?
A Procedure is a group of statements, either predefined by the
compiler writer, or defined by you, that can be called upon
to do a specific job. In this chapter we will see how to
write and use a procedure. During your future programming
efforts, you will use many procedures. In fact, you have
already used some because the WriteString, WriteLn, etc.
procedures you have been using are predefined procedures.
Chapter 5 - Procedures
Examine the program named PROCED1.MOD for ===============
your first look at a user defined PROCED1.MOD
procedure. In this program, we have the ===============
usual header with one variable defined.
Ignore the header and move down to the main program beginning
with line 26. We will come back to all of the statements
prior to the main program in a few minutes.
The main program is very easy to understand based on all of
your past experience with Modula-2. First we somehow write
a header (WriteHeader), then write a message out 8 times
(WriteMessage), and finally we write an ending out
(WriteEnding). Notice that with the long names for the
procedure names, no comments are needed because the program
is self documenting. The only problem we have is, how does
the computer actually do the three steps we have asked for.
That is the purpose for the 3 procedures defined earlier
starting in lines 8, 14, and 20. Modula-2 requires that
nothing can be used until it has been defined, so the
procedures are required to be defined prior to the main
program. This may seem a bit backward to you if you are
experienced in some other languages like FORTRAN, BASIC, or
C, but it will make sense as you gain experience.
HOW DO WE DEFINE A PROCEDURE?
We will begin with the procedure at line 8. We must start
with the reserved word PROCEDURE followed by the name we have
chosen for our procedure, in this case WriteHeader which is
required to follow all of the rules for naming an identifier.
The procedure line must be terminated with a semi-colon.
Following the procedure line, we can include more import
lists, define variables, or any of several other things. We
will go into a complete definition of this part of the program
in the next chapter, just keep in mind that other quantities
can be inserted here. We finally come to the procedure body
which contains the actual instructions we wish to execute in
the procedure. In this case, the procedure body is very
simple, containing only a WriteString and a WriteLn
instruction, but it could have been as complex as we needed
to make it.
At the end of the procedure, we once again use the reserved
word END followed by the same name as we defined for the
procedure name. In the case of a procedure, the final name
is followed by a semicolon instead of a period. Other than
this small change, a procedure definition is identical to that
of the program itself.
When the main program comes to the WriteHeader statement in
line 27, it knows that it is not part of its standard list of
executable instructions, so it looks for the user defined
Chapter 5 - Procedures
procedure by that name. When it finds it, it transfers
control of the program sequence to there, and begins executing
those instructions. When it executes all of the instructions
in the procedure, it finds the end statement of the procedure
and returns to the next statement in the main program. When
the main program finally runs out of things to do, it arrives
at its own end statement and terminates.
As the program executes the for loop, it is required to call
the WriteMessage procedure 8 times, each time writing its
message on the monitor, and finally the main program finds and
executes the WriteEnding procedure. This should be very
straightforward and should pose no real problem for you to
understand. When you think you understand what it should do,
compile and run it.
NOW FOR A PROCEDURE THAT USES SOME DATA
The last program was interesting to show ===============
you how a procedure works but if you would PROCED2.MOD
like to see how to get some data from the ===============
calling program to the procedure, the
program named PROCED2.MOD will illustrate this. We will once
again go straight to the program starting in line number 25.
We immediately notice that the program is nothing more than
one big for loop which we go through 3 times. Each time
through the loop we call several procedures, some that are
system defined, and some that are user defined. This time
instead of the simple procedure name, we have a variable in
the parentheses behind the variable name. In these
procedures, we will take some data with us to the procedures,
when we call them, just like we have been doing with the
WriteString and WriteInt procedures.
When we execute the statement in line 28, because of the
variable named Thing defined in the parentheses, we will take
some data to the procedure named PrintDataOut where it will
be printed. The procedure PrintDataOut starting in line 9
also contains a pair of parentheses with a variable named
Puppy which is of type integer. This says that it is
expecting a variable to be passed to it from the calling
program and it expects the variable to be of type integer.
Back in the main program, we see on line 28, that the program
did make the call to the procedure with a variable named Thing
which is an integer type variable, so everything is fine.
The procedure prefers to call the variable passed to it Puppy
but that is perfectly acceptable, it is the same variable.
The procedure writes the value of Puppy, which is really the
variable Thing in the main program, in a line with an
identifying string, then changes the value of Puppy before
returning to the main program.
Chapter 5 - Procedures
SOME NEW TERMINOLOGY
The parameter in the calling program is referred to as the
actual parameter, and the parameter in the procedure is called
the formal parameter. In this procedure then, the actual
parameter is named Thing, and Puppy is the formal parameter.
Note that this terminology is not unique to Modula-2, but is
used to define terms in many modern programming languages.
Upon returning to the main program, we print out another line
with three separate parts (notice the indenting and the way
it makes the program more readable), then calls the next
procedure PrintAndModify which appears to do the same thing
as the last one. Indeed, studying the procedure itself leads
you to believe they are the same, except for the fact that
this one prefers to use the name Cat for the formal parameter
name. There is one subtle difference in this procedure, the
reserved word VAR in the header, line 17.
VALUE AND VARIABLE PARAMETERS
In the first procedure, the reserved word VAR was omitted.
This is a signal to the compiler that this procedure will not
receive a reference to the actual parameter variable.
Instead, it will receive a local copy of the variable which
it can use in whatever way it needs to. When it is finished,
however, it can not return any changes in the variable to the
main program because it can only work with its copy of the
variable. This is therefore a one-way parameter, since it can
only pass data to the procedure. This is sometimes called a
"call by value" or a "value parameter" in literature about
Modula-2 and a few other modern programming languages.
In the second procedure, the reserved word VAR was included.
This signals the compiler that the actual variable in this
procedure is meant to be passed to the procedure, and not just
the value of the variable. The procedure can use this
variable in any way it desires, and since it has access to the
variable in the main program, it can alter it if it so
desires. This is therefore a two-way parameter, since it can
pass data from the main program to the procedure and back
again. This is sometimes called a "call by reference" or a
"variable parameter" in software literature.
WHICH SHOULD BE USED?
It is up to you to decide which of the two parameter passing
schemes you should use for each application. The "two-way"
Chapter 5 - Procedures
scheme, the variable parameter, seems to give the greatest
flexibility, so your first thought is to simply use it
everywhere. But that is not a good idea because it gives
every procedure the ability to corrupt your main program
variables. In addition, if you use a value parameter in the
procedure definition, you have the ability to call the
procedure with a constant in that part of the call. A good
example is given in lines 12, 20, 30, 34, and 38 of the
present program. If WriteInt were defined with a variable
parameter, we could not use a constant here, but instead would
have to set up a variable, assign it the desired value, then
use the variable name instead of the 5. In addition, when a
value parameter is used, an expression can be used for the
actual parameter such as "Thing + 12". This cannot be done
with a variable parameter because it is not possible to return
a value to an expression.
There is one other reason to use a variable parameter. When
a value parameter is used, a copy of the parameter is made and
the copy is passed to the procedure. If it happens to be a
very large variable, such as a record or an array, (neither
of which we have studied yet), it could be very inefficient
to generate and pass a copy of the entire variable. You may
choose to make it a variable parameter so that the procedure
can refer back to the original data rather than work with a
local copy. You must take care that the procedure does not
accidentally corrupt the original data.
Finally, if a value parameter is used, the actual parameter
and the formal parameter only need to be assignment
compatible, so a single procedure could be used for either
integer or cardinal type data.
BACK TO THE PROGRAM ON DISPLAY, PROCED2
We have already mentioned that both of the procedures modify
their respective local variables, but due to the difference
in "call by value" in the first, and "call by reference" in
the second, only the second can actually get the modified data
back to the calling program. This is why they are named the
way they are. One other thing should be mentioned. Since it
is not good practice to modify the variable used to control
the for loop, (and downright erroneous in many cases) we make
a copy of it and call it Thing for use in the calls to the
procedures. Based on all we have said above, you should be
able to figure out what the program will do, then compile and
Chapter 5 - Procedures
SEVERAL PARAMETERS PASSED AT ONCE
Examine the program named PROCED3.MOD for ===============
an example of a procedure definition with PROCED3.MOD
more than one variable being passed to it. ===============
In this case four parameters are passed to
the procedure. Three of the parameters are one-way and one
is a two-way parameter. In this case we simply add the three
numbers and return the result to the main program. Good
programming practice would dictate the placement of the single
"call by reference" by itself and the others grouped together,
but it is more important to demonstrate to you that they can
be in any order you desire. This is a very straightforward
example that should pose no problem to you. Compile and run
SCOPE OF VARIABLES
The program named PROCED4.MOD is an ===============
illustration of scope of variables, or PROCED4.MOD
where variables can be used in a program. ===============
The three variables defined in lines 6, 7,
and 8, are of course available in the main program because
they are defined prior to it. The two variables defined in
the procedure are available within the procedure because that
is where they are defined. However, because a variable named
Count is defined both places, they are two completely separate
variables. The main program can never use the variable Count
defined in the procedure, and the procedure can never use the
variable Count defined in the main program. They are two
completely separate and unique variables with no ties between
them. This is useful because when your programs grow, you can
define a variable in a procedure, use it in whatever way you
wish, and not have to worry that you are corrupting some other
global variable. The variables in the main program are called
global variables because they are available everywhere.
In addition to the above scope rules, the variable named Apple
in the procedure is not available to the main program. Since
it is defined in the procedure, it can only be used in the
procedure. The procedure effectively builds a wall around the
variable Apple and its own Count so that neither is available
outside of the procedure. We will see in the next chapter
that procedures can be nested leading to further hiding of
variables. This program is intended to illustrate the scope
of variables, and it would be good for you to study it, then
compile and run it.
Chapter 5 - Procedures
A PROCEDURE CAN CALL ANOTHER PROCEDURE
The program named PROCED5.MOD illustrates ===============
procedures that call other procedures. PROCED5.MOD
Study of this program will reveal that ===============
procedure Three starting on line 19 calls
procedure Two which in turn calls procedure One. The main
program calls all three, one at a time, and the result is a
succession of calls which should be rather easy for you to
follow. The general rule is, "any program or procedure can
call any other procedure that has been previously defined, and
is visible to it." (We will say more about visibility later.)
Study this program then compile and run it.
A FUNCTION PROCEDURE
Examine the program named FUNCTION.MOD for ================
an example of a function procedure. This FUNCTION.MOD
contains a procedure very much like the ================
ones we have seen so far with one
difference. In the procedure heading, line 6, there is an
added ": INTEGER" at the end of the parameter list. This is
a signal to the system that this procedure is a function
procedure and it therefore returns a value to the calling
program in a way other than that provided for by parameter
references as we have used before. In fact, this program
returns a single data value that will be of type integer.
In line 16 of the calling program, we find the call to the
procedure which looks like the others we have used except that
it is used in an assignment statement as though it is an
integer type variable. This is exactly what it is and when
the call is completed, the QuadOfSum(Dogs,Cats) will be
replaced by the answer and then assigned to the variable Feet.
The entire call can therefore be used anyplace in a program
where it is legal to use an integer type variable. This is
therefore a single value return and can be very useful in the
right situation. A function procedure can only return a
simple unstructured type. These types are, INTEGER, CARDINAL,
REAL, BOOLEAN, subrange, or enumeration.
A relatively recent addition to Modula-2 allows a function
procedure to return any structured type, not only simple
types. This means that records or arrays can be returned.
This may not be available with your compiler since it is so
One additional point must be made here. If a function
procedure does not require any parameters, the call to it must
include empty parentheses, and the definition of the procedure
Chapter 5 - Procedures
must include empty parentheses also. This is by definition
of the Modula-2 language.
In the procedure, we had to do one thing slightly different
in order to get the return value and that was to use the
RETURN reserved word. Whenever we have completed the desired
calculations or whatever we need to do, we put the result that
is to be returned to the main program in the parentheses
following the return and the procedure will terminate, return
to the calling program, and take the value with it as the
answer. Due to decision making, we may have several return
statements in the procedure but only one will be exercised
with each call. It is an error to come to the end statement
of a function procedure since that would constitute a return
without the benefit of the return statement, and no value
would be returned to the calling program.
WHAT IS RECURSION?
Recursion is simply a procedure calling ================
itself. If you have never been introduced RECURSON.MOD
to recursion before, that definition ================
sounds too simple but that is exactly what
it is. You have probably seen a picture containing a picture
of itself. The picture in the picture also contains a picture
of itself, the end result being an infinity of pictures.
Examine the file named RECURSON.MOD for an example of a
program with recursion.
In the main program, Count is set to 7 and the procedure is
called taking along Count as a parameter. In the procedure,
we display a line containing the value of the variable, now
called Index, and decrement it. If the variable is greater
than zero, we call the same procedure again, this time
entering it with the value of 6. It would be reasonably
correct to think of the system as creating another copy of
the procedure for this call. The variable Index would be
reduced to 5, and another copy of the procedure would be
called. Finally, the variable would be reduced to zero and
the return path from procedure to procedure would be taken
until the main program would be reached, where the program
Rather than making a complete new copy of the procedure for
each recursive call, the same code would be run each time
through and all of the data would be stored away on the
program stack each time through. You have no need to worry
about this because it is all taken care of for you by the
system. You simply call the same procedure as though it were
any other procedure and the system will worry about all of the
details except for one. It is up to you to see that there is
some mechanism by which the process will terminate. If there
Chapter 5 - Procedures
were no decrementing statement in the procedure, this program
would never reach an end and the stack would overflow,
resulting in an error message and termination of the program.
It would be worth your time to remove the decrementing
statement and observe this, after you compile and run it the
way it is now.
Recursion can be very useful for those problems that warrant
its use. This example is a very stupid use of recursion, but
is an excellent method for giving an example of a program with
recursion that is simple and easy to understand. Further
examples of recursion will be given later in this tutorial.
DIRECT AND INDIRECT RECURSION
This example uses direct recursion because the procedure calls
itself directly. It is also possible to use indirect
recursion where procedure A calls B, B calls A, etc. Either
method is available and useful depending on the particular
WHAT IS AN AUTOMATIC VARIABLE?
They are not actually called automatic variables. The term
is borrowed from the C programming language, but they act the
same. When a procedure is called, its local variables do not
exist. They are generated, stored on a stack, used during the
lifetime of the procedure, and disposed of when control is
given back to the calling program. The formal parameters are
also handled in the same way. The variables named Count and
Apple in lines 11 and 12 of PROCED4.MOD are automatic, and so
is Index in RECURSON.MOD. The way these variables are handled
is the reason recursion works. More will be said about this
in chapter 13 of this tutorial.
1. Write a program to write your name, address, and phone
number on the monitor with each line in a different
2. Add a statement to the procedure in RECURSON.MOD to
display the value of Index after the call to itself so
you can see the value increasing as the recurring calls
are returned to the next higher level.
3. Rewrite TEMPCONV.MOD from chapter 4 putting the
centigrade to fahrenheit formula in a function procedure.