


                                                        Chapter 8
                                                      SUBPROGRAMS


LET'S LOOK AT A PROCEDURE
_________________________________________________________________

Ada was designed to be very modular, and we come  ===============
to the point where we will study the first and      PROCED1.ADA
simplest form of modularization, the procedure.   ===============
If you examine the program named PROCED1.ADA,
you will have your first example of a procedure.
Some languages call this kind of subprogram a subroutine, others
a function, but Ada calls it a procedure.


THE PROCEDURE IS LIKE THE MAIN PROGRAM
_________________________________________________________________

Beginning with the executable part of the main program, we have two
executable statements in lines 14 and 15, each calling a procedure
to write a line, if the name has any meaning.  As always, the two
statements are executed in succession, and each statement is a
procedure call to which we would like to transfer control.  The
procedure itself is defined in lines 7 through 11, and a close
inspection will reveal that the structure of the procedure is very
similar to the structure for the main program.  It is in fact,
identical to the main program, and just as we begin executing the
main program by mentioning its name, we begin executing the
procedure by mentioning its name.  The procedure has a declarative
part, which is empty in this case, and an executable part which
says to display a line of text and return the cursor to the
beginning of the next line.

When execution reaches the end statement of the procedure, the
procedure is complete and control returns to the next successive
statement in the calling program.

Everything we have said about the main program, which is actually
a procedure, is true for the procedure.  Thus we can define new
types, declare variables and constants, and even define additional
procedures in the declarative part of this procedure.  Likewise,
we can call other procedures, and do assignments and compares in
the executable part of the procedure.



ORDER OF DECLARATIONS
_________________________________________________________________

The procedure must come after all type, constant, and variable
declarations.  Ada forces you to arrange your program such that all
of the smaller declarations must come first, followed by the larger

                                                         Page 8-1

                                          Chapter 8 - Subprograms

ones.  This is so that the small declarations don't get lost among
the bigger ones.

The embedded procedure has all of the flexibility and requirements
as those defined for the main procedure.  We will give further
examples of each of these points in the next few example programs.
At this time, compile and run this program, and make sure you
understand its operation completely.


THREE PROCEDURES
_________________________________________________________________

Examine the program named PROCED2.ADA, and you    ===============
will see three procedures in the declarative        PROCED2.ADA
part of this program.  Jumping ahead to the main  ===============
program, beginning in line 33, you will see that
the program will write a header, write and
increment something 7 times, then write an ending statement.
Notice how the use of descriptive procedure names resulted in our
understanding of what the program will do without looking at the
procedures themselves.  The names are long, and a bit tedious to
type in, but since Ada was designed to be a language that would be
written once and read many times, the extra time will pay off when
the source code is studied by several persons in the future.


WHAT IS A GLOBAL VARIABLE?
_________________________________________________________________

The variable named Counter, defined in line 10, is a global
variable because it is defined prior to any procedure.  It is
therefore available for use by any of these procedures and it is,
in fact, used by two of them.  In line 14, the variable Counter is
assigned the value of 1 when the procedure named Write_A_Header is
called.  Each time Write_And_Increment is called, the current value
of Counter is written to the display, along with a message, and the
value is incremented.  Note carefully that the procedures are not
executed in the order they are because of their relative order, but
because the main program calls them in that order.  The program
would work exactly the same way if you moved the first procedure,
in its entirety of course, after the procedure
Write_An_Ending_Statement.  The order of execution is controlled
by the order of calls, not the physical order of the procedures.


WHY DO PROCEDURES GO IN THE DECLARATIVE PART?
_________________________________________________________________

The declarative part of the program is where we define entities for
use within the executable part.  The procedures are actually
definitions of how to do something, so they are right where they
belong, but they must come after the smaller declarations.  To
reiterate a rule stated earlier, the variable must be declared

                                                         Page 8-2

                                          Chapter 8 - Subprograms

prior to the procedure declarations so that it does not get lost
among the procedures.  Compile and run this program and be sure you
understand the output.


A PROCEDURE WITH PARAMETERS
_________________________________________________________________

Examine the file named PROCED3.ADA and you will   ===============
find a procedure that requires some data to be      PROCED3.ADA
supplied to it each time it is called.  Three     ===============
variables are defined as type INTEGER and used
in the procedure call in line 28.  We will
ignore the strange looking constructs in lines 12 through 15 for
a couple of paragraphs.  The procedure header, beginning in line
18, states that it is expecting three inputs, and each must be of
type INTEGER, so we are compatible so far.  When the procedure is
called, the value of the first variable, which is named Dogs in
this case, is taken to the procedure, where the procedure prefers
to refer to it by the name Variety1.  In like manner, the value of
Cats is given to the procedure, and is called Variety2.  The
variable named Animals is referred to by the name Total in the
procedure.  The procedure is now ready to do some meaningful work
with these variables, but is somewhat limited in what it can do to
them because of the mode field of the procedure header.


THE MODE OF A PARAMETER
_________________________________________________________________

The formal parameter, as it is called in the procedure header,
named Variety1, is of mode in which means that it is an input
parameter, and therefore cannot be changed within the procedure.
Variety1, along with Variety2 for the same reason, is a constant
within the procedure, and any attempt to assign a value to it will
result in a compile error.  The formal parameter named Total,
however, is of mode out and can therefore have a new value assigned
to it within the procedure.  It is illegal to attempt to read this
variable within the procedure, because it is of output mode only.
If it were defined as being of mode in out, it could be both read
from and written to.  If no mode is given, the system will use mode
in as a default.


PARAMETER MODE SELECTION
_________________________________________________________________

All variables could be defined as mode in out and there would be
no problems, since there would be maximum flexibility, or so it
would seem.  There is another rule that must be considered, and
that rule says that every parameter that is of mode out or in out
must be called with a variable as the actual parameter in the
calling program.  A variable of mode in can use a constant or a
variable for the actual parameter in the calling program.  We have

                                                         Page 8-3

                                          Chapter 8 - Subprograms

been using the New_Line procedure with a constant in it, such as
New_Line(2), and if it had been defined with the formal parameter
of mode in out, we would have had to define a variable, assign the
value 2 to it, and use the variable in the call.  This would have
made the procedure a bit more difficult to use, and in fact,
somewhat awkward.  For this reason, the formal parameter in
New_Line was defined using mode in.  You should choose the mode of
the formal parameters very carefully.

The three formal parameters are available for use in the procedure,
once they are defined as illustrated, just as if they had been
defined in the declarative portion of the procedure.  They are not
available to any other procedure or the main program, because they
are defined locally for the procedure.  When used however, their
use must be consistent with the defined mode for each.


SOME GLOBAL VARIABLES
_________________________________________________________________

The three variables declared in line 10 can be referred to in the
procedure as well as in the main program.  Because it is possible
to refer to them in the procedure, they can be changed directly
within the procedure.  The variable Animals can also be modified
when control returns to the main program because it is declared of
out mode in the procedure header.  This possibility can lead to
some rather unusual results.  You should spend some time thinking
about what this really means.


THE PROCEDURE SPECIFICATION
_________________________________________________________________

Lines 13, 14, and 15, give an example of a procedure specification.
This is an incomplete procedure declaration that you will find
useful when you begin writing larger programs.  The procedure
specification can be included for any procedure, if desired, and
it describes the external interface to the procedure without
declaring what the procedure actually does.  The Pascal programmer
will recognize this as being very similar to the forward
declaration.  More will be said about this topic later.  Note that
the procedure specification is not required in this case, it is
only included as an illustration.

Compile and run this program after you understand the simple
addition and assignment that is done for purposes of illustration.


PROCEDURES CALLING OTHER PROCEDURES
_________________________________________________________________

The example program CALLING.ADA contains examples of a procedure
calling another procedure, which is perfectly legal if the called
procedure is within the scope of the calling procedure.  Much more

                                                         Page 8-4

                                          Chapter 8 - Subprograms

will be said about scope later in this tutorial.  ===============
The only rule that will be mentioned here is        CALLING.ADA
that the procedure must be defined prior to a     ===============
call to it.  You should have no trouble
understanding this program, and when you do, you
should compile and execute it.


HOW DO WE NEST PROCEDURES?
_________________________________________________________________

Examine the program named NESTING.ADA for         ===============
examples of nested procedures.  We mentioned        NESTING.ADA
earlier that it was possible to embed a           ===============
procedure within the declarative part of any
other procedure.  This is illustrated in lines
10 through 21 where the procedure Second_Layer is embedded within
the procedure Triple.  In addition, the procedure Second_Layer has
the procedure Bottom_Layer embedded within its declarative part in
lines 12 through 15.  Such nesting can continue indefinitely,
because there is no limit to the depth of nesting allowed in Ada.


VISIBILITY OF PROCEDURES
_________________________________________________________________

There is a limit on visibility of procedures.  Any procedure is
visible, and can therefore be called, if it is within the top level
of the declarative part of the calling procedure.  Any procedure
is also visible if it is prior to, on the same level, and within
the same declarative part as the calling point.  Finally, any
procedure can be called if it is prior to, on the same level, and
within the same declarative part as any subprogram within which the
calling point in nested.  In simpler words, a procedure is visible
in three cases.  First, if it is within the declarative part of the
calling procedure.  The second case is if it is a peer (on the same
level within a parent subprogram) or thirdly, if it is a peer of
any parent.

The procedure named Triple can therefore call Second_Layer, but not
Bottom_Layer, since it is at a lower level and is not visible.  The
main program, according to these rules, is only allowed to call
Triple, because the other two procedures are nested too deeply for
a direct call.  Be sure to compile and run this program and study
the results.


ADA FUNCTIONS
_________________________________________________________________

The program named FUNCT.ADA has two examples of Ada functions.  A
function differs from a procedure in only two ways.  A function
returns a single value which is used in the place of its call, and
all formal parameters of a function must be of type in, with no

                                                         Page 8-5

                                          Chapter 8 - Subprograms

other mode permitted.  In the program under       ===============
consideration, two functions are illustrated,        FUNCT.ADA
one beginning in line 16, and the other           ===============
beginning in line 21.  Note that each begins
with the reserved word function.


A FUNCTION SPECIFICATION
_________________________________________________________________

In a manner similar to that defined for a procedure we can define
a function specification that gives the interface of the function
to the outside world.  You will find the function specification
useful later in your Ada programming efforts.  It is similar to the
Pascal forward declaration.  Note once again, that the function
specification is not required, it is only given here as an
illustration.

The function named Square requires one argument which it prefers
to call Val, and which must be of type INTEGER.  It returns a value
to the main program which will be of type INTEGER because that is
the type given between the reserved words return and is in the
function body.

A function must return a value, and the value is returned by
following the reserved word return with the value to be returned.
This return must be done in the executable part of the program, as
illustrated in line 18.  It is an error to fail to execute a return
statement and fall through the end of a function.  Such a runtime
error will be reported by raising the exception Program_Error,
which will be explained later in this tutorial.


CAN YOU RETURN FROM A PROCEDURE?
_________________________________________________________________

It would be well to point out that you can return from a procedure
by using the return statement also, but no value can be given since
a procedure does not return a value in the same manner as a
function.  The return statement can be anyplace in the procedure
or function and there can be multiple returns if the logic dictates
the possibility of returning from several different places.


A VALUE IS SUBSTITUTED FOR THE FUNCTION CALL
_________________________________________________________________

Examining the executable part of the program, we find that the
variable Twelve is initialized to the value of 12, and this
variable is used in line 28 as the argument for the function
Square.  This causes Square to be called, where the value of 12 is
squared and the result is returned as 144.  It is as if the
resulting value of 144 replaces the function call Square(Twelve)
in the Put procedure call, and the value of 144 is displayed.

                                                         Page 8-6

                                          Chapter 8 - Subprograms

Continuing on to line 30, the variable Twelve, which is still
assigned the value of 12, and the constant 12, are given to the
function Sum_Of_Numbers which returns the sum of 24.  This value
is assigned to the variable named Sum where it is stored for use
in line 32.

A function can be defined with no input parameters, in which case,
the function is called with no parameters.  Such a case would be
useful for a random number generator where the call could be X :=
Random; assuming a new random number is returned each time the
function is called.  Compile and execute this program and study the
output generated.


ANOTHER NOTE ABOUT THE DERIVED TYPE
_________________________________________________________________

As mentioned in chapter 6, a derived type with INTEGER as parent
could be declared in line 14, and the derived type would inherit
all operations available with INTEGER, including the ability to be
used as the formal parameter for the function Square.  This means
that the function Square could be called with a variable of either
of the two types.  In order to do this, it is necessary to use a
function specification so that the function body will follow the
derived type declaration.  Ada requires all smaller declarations
to be made before subprogram bodies, so they do not get lost among
the larger definitions.


A FULLER EXAMPLE
_________________________________________________________________

Examine the program named ODDSQRE.ADA which is    ===============
a rather odd program that computes the square of    ODDSQRE.ADA
an integer type variable, but maintains the sign  ===============
of the variable.  In this program, the odd
square of 3 is 9, and the odd square of -3 is
-9.  Its real purpose is to illustrate several procedures and a
function interacting.

The main program named OddSqre has a function and a procedure
nested within its declarative part, both of which have parameters
passed.  The nested procedure named Square_And_Keep_Sign has
another procedure nested within its declarative part, named
Do_A_Negative_Number which calls the function declared at the next
higher level.

This program is a terrible example of how to solve the problem at
hand but is an excellent example of several interacting
subprograms, and it would be profitable for you to spend enough
time with it to thoroughly understand it.




                                                         Page 8-7

                                          Chapter 8 - Subprograms

COMMENTS ON ODDSQRE.ADA
_________________________________________________________________

This program illustrates some of the options that are purely
programming taste.  The first option is illustrated in line 24,
where we could have chosen to use the construct Number_To_Square**2
instead of the simple multiplication.  Either form is correct and
the one to be used should reflect the nature of the problem at
hand.  The second option is the fact that three returns were
included in lines 39, 42, and 45, when a single return could have
been used following the end of the if statement.  This was done to
illustrate multiple returns in use.  In some cases, the logic of
the program is much clearer to use several returns instead of only
one.  More than anything else, it is a matter of personal taste.
Be sure to compile and execute this program.


OVERLOADING
_________________________________________________________________

We have casually mentioned overloading earlier   ================
in this tutorial and it is now time to get a       OVERLOAD.ADA
good example of what overloading is by examining ================
the program named OVERLOAD.ADA.  This program
includes two functions and two procedures and
all four of these subprograms have the same name.

The Ada system has the ability to discern which subprogram you wish
to use by the types included in the actual parameter list and the
type of the return.  In line 48, we make a call to a function with
a 2, which is an integer type constant, and we assign the returned
value to an INTEGER type variable.  The system will look for a
function named Raise_To_Power with a single integer class formal
parameter and an INTEGER type return which it finds in lines 14
through 18, so it executes this function.  The actual searching
will be done at compile time so the efficiency is not degraded in
any way by the overloaded names.

If you continue studying this program you will see how the system
can find the correct subprogram by comparing types used as formal
parameters, and the type returned.  Using the same name for several
uses is referred to as overloading the subprogram names and is an
important concept in the Ada language.


OVERLOADING CAN CAUSE YOU PROBLEMS
_________________________________________________________________

If we made an error in this example program, by inadvertently
omitting the decimal point in line 50, and assigning the result to
an integer type variable, the system would simply use the wrong
function and generate invalid data for us.  An even worse problem
could be found if we had a function that used an INTEGER for input
and a FLOAT for output, because only one small error could cause

                                                         Page 8-8

                                          Chapter 8 - Subprograms

erroneous results.  Because of this, it would be to your advantage
to use different subprogram names for different operations, unless
using the same names results in clear code.

In the case of the text output procedures which we have been using,
it makes a lot of sense to overload the output subprograms to avoid
confusion.  The name Put is used for outputting strings, integers,
enumerated types, etc, and we are not confused.  Overloading can
be an advantage in certain cases but should not be abused just
because it is available.  Be sure to compile and execute this
program.


PROGRAMMING EXERCISES
_________________________________________________________________

1.   Rewrite TEMPCONV.ADA to do the temperature conversion in a
     procedure.

2.   Rewrite TEMPCONV.ADA to do the temperature conversion in a
     function.

3.   As mentioned in the text, add a function to the program
     OVERLOAD.ADA that uses an INTEGER for input and returns a
     FLOAT type result.  Remove the decimal point from line 50 to
     see that the new function is called when we really intended
     to call the procedure with the floating point number.



























                                                         Page 8-9