There are two types of routines: procedures and functions. Both have an optional parameter list. Functions return one or more values.
All parameters are passed by value. (Note that variables always hold references, so a reference is passed by value, resulting in semantics of pass by reference for the objects themselves.)
Parameters cannot be used to return values directly. (There are no "var" parameters.) The only way to return a value is via a function result. Thus procedures do not return any values (but they can change the state of an object they reference). A function can return more than one result.
BNF:
routine-decl ::= identifier [ "(" parameter-list ")" ]
[ "->" "(" parameter-list ")"] is
routine-impl
parameter-list ::= param-decl {"," param-decl }
param-decl ::= identifier ":" type-def
routine-impl ::= deferred routine-spec
| builtin routine-spec
| [ redefined ] routine-body
routine-body ::= routine-comment
[ pre condition ]
[ const const-decls ]
[ var var-decls ]
do
statement-list
[ post condition ]
end identifier
routine-spec ::= routine-comment
[ pre condition ]
[ post condition ]
end identifier
A short function is
count -> (val: Integer) is
== Return number of elements
do
val := i_count
end count
A simple procedure without parameters, pre or post conditions:
show is
== displays this element on standard output do
print ("Name: ", name, "\n")
print ("Count: ", count, "\n")
end show
A function with two parameters and two return values:
lookup (n: Integer, show: Boolean)
-> (nm: String, cnt: String) is
== returns name and count for entry number n
== n: number of entry to lookup, must be valid
== show: if true, value is also printed to standard out
== nm: returns the name of the entry
== cnt: returns the number of the entry pre
n >= 1
and n <= nr_of_entries do
nm := entries[n].name
cnt := entries[n].count
if show then
entries[n].show
end if post
nr_of_entries = old nr_of_entries end lookup
The comment after the function header is part of the language. Comments for routines are not optional and are part of the routine interface.
Pre- and post conditions are optional.
Values are returned by assigning to the result variable (nm and cnt in this example). Result variables are initially undefined and must be assigned a value during function execution. It is an error to return from a function with undefined result variables. A function without an assignment statement to a result variable will produce a compile time error; a function that contains but does not execute such a statement results in a runtime error.
Routines are visible in the whole class scope. This means that a routine call may precede the routine definition, allowing mutually recursive routines.
Many people hold the view that functions should not have side effects. Suggestions have been made to us that the language should not allow side effects in functions (by inhibiting assignments to instance variables and routine calls to objects held in instance variables). While this is technically possible, it is impractical. We agree that it is a valuable design rule that functions should not have side effects, but side effects in this context can only mean changes to the external state of the object. Sometimes side effects that change the internal state of the object are desirable. An example is the implementation of a query object with caching. A query (a function) should not change the external state of the object, but it may well result in some information being cached inside the object (thus changing the state of the object - a side effect). If this change does not affect the externally visible behaviour (other than in efficiency), we say that the external state has not been changed. Our design rule is thus satisfied. The compiler cannot distinguish whether changes of state are internal or external. Thus this valuable design rule remains unenforced by the compiler.
For details on pre- and postconditions, see section 12.
For details on redefined routines, see section 16.2.
For details on deferred routines, see section 16.4.
Builtin routines are written with the keyword builtin after the routine header:
BNF:
routine-decl ::= identifier [ "(" parameter-list ")" ]
[ "->" "(" parameter-list ")"] is
routine-impl
routine-impl ::= builtin routine-spec
| ...
routine-spec ::= routine-comment
[ pre condition ]
[ post condition ]
end identifier
Example:
cursorTo (x: Integer, x: Integer) is builtin
== Set the terminals cursor to screen position (x,y).
pre
x >= 0 and x <= width and
y >= 0 and y <= height end cursorTo
The builtin keyword is used only by system programmers writing classes that are part of the Blue environment. It cannot be used by application programmers. Builtin classes are usually implemented in a system-specific way (often in another language than Blue).