Keywords: routine, procedure, argument, function, Result
A routine is a named piece of code. When the name is encountered during code execution, the routine is called: data is passed to the routine, and the code in the routine is executed. After the routine has executed, control returns to the location where the routine was called. Data can be passed to a routine from the caller through an argument list. The caller supplies actual values, and the formal arguments in the routine header are bound to the actual arguments, in serial order. Actual and formal arguments must agree in number, order, and type; names are irrelevant. The value of a formal argument cannot be changed.
A procedure changes one or more values and returns nothing. The only way to get data back from a routine is to use a function: a function returns a value and changes nothing.
A routine is a named piece of code. Consider the make routine; the name of the routine is make, and the routine contains one or more lines of code. When an Eiffel system executes, the make routine of the root class is found and executed. One routine can call other routines, so the flow of control moves from one routine to another, until all the called routines have been executed. Control then returns to the root make routine, that routine terminates, and control returns to the user.
The calling diagram below shows three levels of routine calls. At the top level, the routine make is called (1). This routine contains the names of three other routines, each of which is called in turn. The read routine is called (2), the code in that routine is executed (3), it calls no other routines, so control returns to the location in make just after the read routine was called (4). The name update is then seen so the update routine is called (5), and in turn calls the routines simple and complex; neither of these call other routines. The simple routine is called, executed, and control returns to the update routine (6-8). The complex routine is then called (9), the code in that routine executes (10), control is returned to update (11), that routine finishes, and control returns to make (12).
The make routine then calls display (13), that routine executes (14), and returns control to make (15). The make routine has now been completely executed, so the system has been fully executed and control returns to the user (16).
The code for this sequence of routine calls is shown below. Only the names of the routines, and the routine headers, are shown; routine code that does not contain a routine call is indicated by ... . The routines are shown below in a concise format; note that you cannot write Eiffel code in three columns as shown below in the illustration. When the make routine is executed, the following events occur. make calls read, then update, then display. read calls nothing. update calls simple and then complex. simple calls nothing, complex calls nothing. display calls nothing. The order of code execution is found by tracing out each routine call, and executing the code in each routine, in serial or listed order.
make is
do
read
update
display
end --
make
read is
update is
display is
do
do
do
...
simple
...
end
-- read
complex
end -- display
end -- update
simple is
complex is
do
do
...
...
end -- simple
end -- complex
3.2 Routine syntax and mechanism
A routine is called when the name or identifier of the routine is encountered in the code. Eiffel finds the routine definition in the class, and executes the code in the routine. When all the code has been executed, the routine exits and control is returned to the caller, immediately after the routine call. This code encapsulation is fundamental to code reuse, because the named code can be executed as a single instruction (the routine call). The code in the routine does not have to be re-written every time the user wants to execute it; the routine is written once, and then called as needed. There are two kinds of routine, named a procedure and a function. The two types of routine behave differently, so care must be taken in deciding which to use.
A routine definition consists of two main parts, the routine header and the routine body. The routine header defines the name and signature of the routine, by listing the type of each data value received and returned to the caller. The routine body provides the code that is executed in the routine. Comments and local variables are listed after the header and before the body.
The standard layout of a routine in steps of four spaces is
1 step header
3 steps header comment
2 steps do
3 steps body (executable code)
2 steps end
A routine can have several names; one name is often a shorter form of the other, more meaningful name. When this occurs, the names are simply listed in the routine header before the arguments, separated from each other by a comma. The routine can then be called by any of these names.
The format of a procedure is
name (arguments) is
-- description of change
local declarations
do
routine code
end -- name
a name (possibly followed by other names for the procedure)
any arguments to the procedure, enclosed in round brackets
the keyword is
a header comment
any local variables
the body of the procedure, enclosed in the keywords do and end
the name of the procedure as a comment.
The procedure header consists of a name, followed by any input arguments in brackets, and the keyword is. The name of a procedure is a verb that describes what the procedure does; specifically, that describes the change made by the procedure. The header comment describes what the procedure does with no mention of how this behaviour is implemented in the routine body. Any local variables are then declared after the keyword local; a local variable exists only within the routine. The procedure body is then coded, first the keyword do, then the routine code, then the keyword end. The name of the procedure is then written as a comment after the end. The indentation for each part of the procedure is shown below; note that the comment is indented to the level of the code, not the level of the do and end.
The following convention is used for names of procedures in the case study:
get: read then set
read: prompt then read with io.readX
set: store a value, often from io.lastX
show: show a value, possibly with label.
A procedure is a routine that changes one or more values. A procedure is used if and only if you change the value of an attribute in the procedure. If a new value is to be calculated, then a function is used to calculate and return that value. Input and output code must be placed in a procedure and not a function, because input changes the value of the relevant input buffer, and output changes the "value" of the screen.
Common error: Two routines have the same name, generating a name clash
Error code: VMFN
Error: two or more features have same name
What to do: if they must indeed be different features, choose different names or use renaming
A local variable is a variable that is declared in a routine. The format of a local declaration is simply the word local, followed by whatever declarations are needed. If there is only one local variable, the word local and the declaration are written on the same line. If there are multiple declarations, then the word local is written on a line by itself, and the declarations are written on the following lines, indented four spaces from the word local.
An example of a local declaration in a routine is shown below. The local variable amount is declared at the top of the routine, and used to store the input value. Once the value has been stored in this variable, it is passed as an argument to the deposit routine.
example
is
-- show how to use a local variable
local amount: REAL
do
io.putstring ("Enter amount to deposit: ")
io.readreal
amount := io.lastreal
deposit (amount)
end -- example
A local variable exists only while the routine is being executed. It is created when the routine is called, given its initial or default value, used in the routine, and destroyed when the routine exits. A local variable thus cannot be referenced outside of its routine. Compare this to an attribute. An attribute is a variable declared in a class, and exists while the system is being executed. This notion of existance is made precise by the term scope. The scope of a variable is the part of a program where that variable can be seen or referenced. An attribute can be seen and used by any routine in its class, so the scope of an attribute is its class (but see Section 5.4). A local variable can only be used in its routine, so the scope of a local variable is its routine.
Two identifiers with the same name cannot have the same scope; this is a name clash. If you write code with a name clash, Eiffel cannot work out from the name which variable you want, so it gives up and your compilation fails. Two attributes in the same class cannot have the same name. A local variable cannot have the same name as an attribute, because they share the same scope in the routine body. Two local variables in the same routine cannot have the same name. Two local variables in different routines can have the same name, because their scopes do not overlap so there is never any confusion about which to use in which routine.
Common error: name clash between local and feature
Error code: VLRE (1)
Error: local entity has same name as feature of class
What to do: change the name of the local entity, or of the feature
There are now two ways to store data, in an attribute and in a local variable, so we must ask "When should you use an attribute, and when should you use a local?". The answer is simple: use a local variable whenever you can. When writing code, start by coding every data value as a local variable. If a value is used by two routines, then you are forced to store the value as an attribute. Attributes are there to store the state of the object, not to make the code efficient. The number of attributes should be kept as small as possible, and the way to do this is to use local variables wherever this is possible.
When the name of a routine is encountered during code execution, control is transferred to that routine; we say the routine is called. It is possible to pass data values from the caller to the called routine, to be used inside the called routine. A data value passed to the routine is called an argument to the routine. The data supplied by the calling routine are the actual arguments, the actual values used in that routine call. The variables that store this data in the routine are called formal arguments, because they define the formal behaviour (signature) of the routine. A formal argument is a local variable.
An example of the calling and the called code with arguments is shown below. The make routine (among other things) reads in a value from the user, stores the value in the local variable this, and passes this as an actual argument to the procedure add. The procedure receives the actual argument, and stores its value in the new, local variable new; new is the single formal argument to the routine add. The procedure then changes the value of sum, by adding the new number to it.
feature
sum: REAL
make is
local this: REAL
do
...
this := io.lastreal
add (this)
end -- make
add (new: REAL) is
-- add the new number to the sum
do
sum := sum + new
end -- add
Data is passed from the caller to the called routine through an argument list. The calling code supplies a set of values to be passed; call these the actual arguments. The routine header has a matching list to receive these values; call these the formal arguments. The formal argument list in the routine header consists of one or more variable declarations; declarations of different types are separated by semi-colons. Some examples of routine calls and the routine headers with arguments are
deposit (43.60)
-- actual argument is the value 43.60
deposit (number: REAL) is
-- one formal argument of type REAL
gcd (45, 35)
-- actual arguments are the values 43 and 35
gcd (this, that: REAL) is
-- two formal arguments of type REAL
do_something (42, 64, 83.7, 0.0001, y)
-- actual arguments are the five values ...
do_something (a, b: INTEGER; c, d: REAL; e: CHARACTER)
is
-- five formal arguments of types ...
When the routine is called, each formal argument is bound to the corresponding actual argument. Argument binding is simple: each formal argument is created as a local variable and given the value of the actual argument, when the routine is called. The first formal argument is bound to the first actual argument, the second formal argument is bound to the second actual argument, and so on until all the arguments are bound. Binding is done purely on the order in which the arguments occur. The name is irrelevant; only the shape, defined by the argument list, matters when the actual and formal arguments are bound. The actual and formal arguments must therefore agree in number, order and type or the two argument lists cannot be bound. Once a formal argument has been bound, you are not allowed to change its value; if you try, the system will not compile.
The calling code supplies values to the routine, to be used in the routine. The type of the value that is passed is defined in the routine header by the formal argument, and this is the only constraint on the argument. The calling code can supply a literal, a constant, a variable, an expression, or a function as its value; all the called code cares about is that the supplied value be of the defined type. A routine header and several legal routine calls are shown below:
add (number: REAL) is
add (3)
-- INTEGER literal, converted to heavier REAL
add (3.6)
-- literal of type REAL
add (Pi)
-- constant of type REAL
add (this)
-- variable of type REAL
add (this + 32 - 4 * that)
-- expression that evaluates to a REAL value
add (sqrt (this))
-- function that returns a REAL value
Common error: a formal argument has the same name as a feature in the class, a name clash
Error code: VRFA
Error: Formal argument has same name as feature of the class
What to do: Change the name of the argument, or that of the feature
Common error: a formal argument has the same name as a local in the feature, a name clash
Error code: VRLE (2)
Error: local entity has same name as formal argument of the same routine
What to do: Change name of local entity, or of argument
Common error: the number of actual and formal arguments do not match
Error code: VUAR (1)
Error: wrong number of actual arguments in feature call
What to do: make sure that number of actuals matched number of formals
Common error: the type of the actual and formal arguments does not match
Error code: VUAR (2)
Type error: non-conforming actual argument in feature call
What to do: make sure that type of actual argument conforms to type of corresponding formal argument.
Explanation: "conform" means roughly "of the same type"; for the moment, assume that it means "same or heavier type". Two variables of the same type conform, INTEGER conforms to REAL, and REAL conforms to DOUBLE, so INTEGER conforms to DOUBLE. A more precise definition is given in Section 11.2.
A function has a type and returns a value, like an attribute. A function calculates a value and changes nothing, so a function is used when you calculate a new value from existing values.
The format of a function is
name (arguments): TYPE is
-- description of value
local declarations
do
routine code
Result := expression
end -- name
a name (possibly followed by other names for
the function)
any arguments to the function, enclosed in
round brackets
the type of value returned by the function,
written after a colon and a space
the keyword is
a header comment
any local variables
the body of the function, enclosed in the
keywords do and end
the name of the function as a comment.
A function header starts with the function name, followed by any input arguments. The name of a function is a noun, that describes the value returned by the function The type of the returned value is then given, preceded by a colon. In contrast, a procedure has no return type because a procedure does not return a value. The function header is terminated by the keyword is. The function comment is then written, followed by any local declarations and the function body enclosed in do and end. The indentation for each part of a function is shown below:
When a function is called, its formal arguments are bound to the actual arguments. In addition, a special local variable called Result is created to contain the functions value; the type of Result is given in the header as the returned type. On entry to the function, Eiffel creates a variable of that type with the appropriate initial value. At some point in the function body, this variable is usually given a more useful value. If the value is not changed, then Result still has its initial, default value. When the function returns control to the caller, the value of the function is whatever value is stored in Result. The variable Result is like any local variable, except that its value on exit is the value of the function. You can use the variable to store a value, or to provide a value; in particular, you can do such things as Result := Result + value.
Several functions have conventional names in the case study. These are
valid: a BOOLEAN function that returns true if a value is valid
finished: a BOOLEAN function that returns true if the user has chosen to finish
Many standard arithmetic functions, such as sqrt (square root) and sine (sin of an angle) are functions that receive a single value and return a single value. Examples of function calls are shown to the left below, and the function header is shown to the right. Without knowing anything about how these functions are implemented, the function headers can be defined for these routines, because the signatures are known; a single REAL value is passed in, and a single REAL value is passed back.
&nb