|
|
Developed
at the Polish-Japanese Institute of Information
Technology © Copyright by ODRA team, © Copyright by PJIIT |
|
|
|
ODRA – Object
Database for Rapid Application development Description and Programmer Manual |
||
|
by Radosław Adamus and
the ODRA team |
||
8. SBQL ProceduresODRA procedures are determined by a source
code (as usual) and by the runtime environment. During runtime, the
procedures are special complex objects stored inside modules. Such procedures
are globally available. Procedures stored inside classes are named “methods”
and called in a class instance context. Procedures can also be components of
updatable views, where they play a special role. Procedures encapsulate
arbitrary complex computation and can be called many times in many places of
the code. Their behaviour can be parameterized by the use of parameters
and/or by the state of the execution environment. For all kinds of procedures
we assume that local procedure objects and their actual parameters are
invisible from outside and are invisible for called procedures; thus each
procedure creates its own local name space. This is a typical assumption for
practically all programming languages, greatly supporting reuse and
independent work of programmers. 8.1 Procedures and Functional Procedures
In ODRA there is little
distinction between procedures and functional procedures. Semantically, the
only difference is that a functional procedure returns an output on its
invocation; thus, an invocation can be treated as a query and can be nested
as a part of other queries. A non-functional procedure cannot be a part of a
query because it returns no value. Typing of procedures and functional
procedures is a bit different: the type of a proper (non-functional)
procedure does not contain a return type. Each procedure is uniquely
identified by its name. Overloading of the names is not supported. The result
of a functional procedure is typed, similarly to other programming languages.
Each functional procedure can also be used as a proper procedure; in this
case its return value is neglected. Procedure declaration syntax
A procedure declaration consists
of a procedure name, a typed parameter list, a type of its output and a list
of imperative SBQL statements. name([parameter_list])[:returntype] {statement_list} parameter_list ::= par_declaration [;parameter_list] Procedure parameter declaration
syntax determines its name, type and cardinality: par_declaration
::= name:type [ cardinality ] If the cardinality is not
specified, the default cardinality [1..1] is assumed. In the procedure
signature parameter declarations are separated by semicolons. Procedure invocation syntax name([actual_par_list]) actual_par_list ::= parameter[;actual_par_list] parameter ::= query 8.2 Parameters of Procedures
Procedures can possess parameters.
A parameter specification determines its formal name, a parameter type and a
cardinality. The parameter passing technique implemented in the ODRA system
is known (e.g. from C) as strict-call-by-value.
It means that each actual parameter determined by a query is evaluated before
the procedure execution and the result is stored at the procedure activation
record as a binder which name is the name of the corresponding formal
parameter. Thus, (like Pascal and unlike Java or C/C++) procedure parameters
do not have the “local variable” semantics. If the result of an
argument query is a value, it is passed directly as a (named) value (passing
by value). If the result of an argument query is an object reference it will
be passed directly as a (named) reference (passing by reference). This
parameter transmission method combines call-by-value
with call-by-reference in a very
general fashion, which allows the programmer to pass as a parameter the result
of any complex query that combines atomic values, references, auxiliary
names, structures, bags, sequences, etc. 8.3 Local VariablesLocal variables (objects) can be
declared within procedures and within statement blocks. According to the
scope rules the visibility of each declaration is constrained to its
declaration block and sub-blocks (unless it is overwritten by a new
declaration). See the variable declaration section for the syntax of a local
variable declaration. Inside a procedure body a local
variable can be created by a create
statement (see the object creation section). A locally created object can be
declared inside the current code block or in any visible environment (e.g. an
outer block and a module). 8.4 Return from a Functional Procedure
To return a value from a
functional procedure the return
statement must be used. The return value is determined by a query. The syntax
is as follows: return query; The statement causes immediate
terminating the procedure. The query can be any SBQL query returning a result
that conforms to the return type in the procedure specification signature.
Returning references to local variables is forbidden (rejected by the dynamic
type checking). The return value can be also built
up from a query that returns a named result (see: auxiliary names). For
example, the statement: return (Emp where worksIn.Dept.name = “PR”) as PR_Staff; returns references to employees
working in the PR department. The
references are named PR_Saff. The
name is available outside the procedure body. 8.5 Examples of Procedures
A functional procedure add returns the sum of two integer
values passed as parameters. add(a:integer;b:integer):integer { return a + b; } Usage: add(12; 5); add(5 + count(Emp); 64) – 10; A proper procedure giveRise rises salaries of employees
(passed as parameter emp, which is
a bag of references) with a value (passed as parameter value). The procedure returns no value. giveRise(emp:ref Emp[0..*]; value:integer) { foreach
emp as e do { e.sal := e.sal + value; } } Usage: giveRise(
Emp where worksIn.Dept.dName =
“PR”; 200); 8.6 Functional Procedures as Database ViewsA procedure lowEarning for each employee earning less that the average and
having the position within the pos
parameter (bag of strings) returns a structure containing three named fields:
employee name N, salary S and
department name D). lowEarning(pos:string[1..*]): record{N:string; S:integer; D:string;}{ a : real; a := avg(Emp.sal); return
(Emp where sal < a and pos contains position). (lName as N, sal as S,
worksIn.Dept.dName as D); } Usage: lowEarning(“bricklayer”).(N,
S, D); lowEarning(bag(“programmer”,
“designer”)).(N, S); If a procedure returns a
collection of records with named fields, it is similar to the concept that in
databases is known as view. For
instance, an invocation of the procedure lowEarning can be as follows: (lowEarning(“programmer”)
where
N = “Brown” and D =
“Toys”).S The query returns the salary of a
low earning programmer named Brown from the Toys department. Because such a
procedure can return references, it can also be used for updating, for
instance: foreach(lowEarning(“programmer”) where
N = “Brown” and D =
“Toys”) do S := S +
200; Although ODRA does not forbid such
updates, they should be used with care due to the view updating anomalies,
which may warp the programmer intention. In case of doubts the programmer is
recommended to use SBQL updatable views rather than functional procedures. 8.7 Recursive Procedures and Methods
In ODRA recursive procedures do
not imply any special syntax. All procedures can be recursive. Such a feature
is common in popular programming languages implementations. ODRA SBQL extends
the property to query languages. This allows for smart specification of some
inherently recursive tasks. The use of recursive procedures needs some
attention from the programmer concerning the following aspects: ·
Depth of the recursion: SBQL does not limit it physically, but too
large depth may compromise performance. ·
The depth cannot be infinite, hence the programmer is responsible for
ensuring that the recursion eventually will be terminated. ·
Performance: in many cases iterations or transitive closures are
faster than recursive procedures. Example factorial(n:integer):integer { if n = 0 or n = 1 then return 1; return
n * factorial(n - 1); } |
|
Last modified: June 28, 2008 |