|
|
|
Reference Manual of LoI - Variables, Parameters and Typing |
|
|
Variables are represented by identifiers. They range over all basic
and composed types. Variables which describe roles are handled in 3.6.3.
Variables have to be declared, before they can be used or
modified. References to variables are constrained to the scope in
which they have been declared.
A variable is declared by a definition or an introduction. A definition has the form
<the variable given by its name> = <some expression>;
This has some consequences. The variable may now be used (inside the
given scope), it has a value (the result of expression) and a type
(which depends on the type of the expression). If another variable
with the same name is already declared, the given definition just
overrides (but does not change the value of) the other variable. All
references in the scope to the name are now resolved by the variable just defined.
For function definitions it is also possible to use the following form
(cf. 3.5.16 for the construction of user-defined
functions):
<the variable given by its name> <parameters> = { <some statement> };
This is only syntactic sugar for
<the variable given by its name> = <parameters> => { <some statement> };
A variable can be introduced as part of a parameter list (cf. 3.4.5 and
3.9.2).The effects are similar to a definition (except
that the value and type may not be clear immediately).
Finally, some variables (especially module names) are defined outside
the current module. These have to be declared foreign in the module header:
foreign <a variable>, <a variable>,....., <a variable>
The type of these declared variables has to be matched with their foreign counterpart. Note, that there is only one global foreign namespace.
A variable which has been declared or introduced may be modified. A modification is either an assignment, a decrement, or an increment.
An assignment has the form (cf. 3.5.12.3 for assigning values to components of arrays and cf. 3.5.14.3 for those of tuples)
<the name of the variable> : = <some expression>
The value of the variable will be changed to the result of the expression. Remark: The type of the variable may not be changed; it will be unified with the type of the expression.
An increment has the form
<a variable of type int>++
The given variable is incremented by one. If the maximal value is passed, an overflow may occur.
A decrement has the form
<a variable of type int>- -
The given variable is decremented by one. If the minimal value is passed, an underflow may occur.
Variables may be used in expressions (cf. 3.5).
The scope of a variable is that part of the module, in which
an reference in the form of the name of the variable will result in
the variable itself.
The scope of a variable is limited to the block (and all subblocks), in
which it has been declared. Furthermore, only backward references are
allowed.
|
The reference to v0 in the assignment is valid, but not that to
v1 (considering that v1 is not declared elsewhere). This
also means, that recursive references like v = v are not valid.
There is one exception of this rule: in functions definitions
recursive references may be used (cf. 3.5.16.2).
Parameters comprise either single identifier or a list of identifiers separated by commas and enclosed in parentheses:
< an identifier > (< an identifier >,....,< an identifier >) |
Parameters have the same properties as variables
declared and used in the body (cf. 3.4).
The parameters-form can be used in module definitions (
cf. 3.3),
function definitions (cf. 3.5.16.2),
input/composed utterances
(cf. 3.9.2 and 3.9.4)
or type definitions (cf. 3.4.9). The scope
of each parameter ranges in all cases except utterances over the whole
entity. With regards to utterances the scope of a parameter is like a
variable (cf. 3.4.4).
Variables and expressions are typed (This also includes the
statements which pass or receive a value like return, input
utterances, output utterances, throw and try).
The type of a variable (and an expression) is either be determined by
a type constraint (cf. 3.4.7) or the context, in which it is
used. E.g.:
R ? v::int
The type of the variable v is set by the constraint: it can only be
int. The variable R has to be of type Role because of the
requirements of an input utterance.
All values or variables for which it is not possible to determine
their type are assumed to be of type Object.
A variable can not change its type - except it is redefined like in:
|
In some cases it is useful to constrain the type of a variable or an expression (e.g. documenting the interface of the interaction). Type constraints may be used for formal parameters of functions and in expressions. In both cases the syntax is similar:
<a variable> :: <a type expression> <an expression> :: <a type expression> |
The effect of this operator is that the type of the given expression or parameter has to be equal to that of the type expression. If the type expression is a function type or a parameterized type, that type has to be enclosed in parentheses.
All possible type expressions are shown in this table:
| Type (Schema) | Description |
|---|---|
| void | the empty type (cf. 3.5.2) |
| agent | type of agent values (cf. 3.6.1) |
| aid | type of agent identifiers (cf. 3.6.2) |
| constructor | type of constructor values/markers (cf. 3.5.15) |
| bool | type of boolean values (cf. 3.5.5) |
| module | type of module values (cf. 3.5.10) |
| mutex | type of mutex values (cf. 3.5.7) |
| int | type of integers (cf. 3.5.3) |
| float | type of floats (cf. 3.5.4) |
| object | type of unspecified values (cf. 3.5.1) |
| char | type of characters (cf. 3.5.6) |
| port | type of port values (cf. 3.5.11) |
| role | type of role values (cf. 3.6.3) |
| string | array of characters (cf. 3.5.13) |
| thread | type of thread values (cf. 3.8) |
| <identifier> | a user defined type alias (cf. 3.4.9) |
| <identifier> <type expression> | a user defined parameterized type (cf. 3.5.12) |
| { <type expression> } | an array type (cf. 3.5.12) |
| <type expression> => <type expression> | a function type (cf. 3.5.16) |
| (<type expression>) | grouping of a type |
| (<type expression>, <type expression>,....., <type expressions>) | a tuple type /cf. 3.5.14) |
Note, that if a component of a function type or parameterized type is another function type or parameterized type, that component has to be enclosed in parentheses.
It is possible to define new types using primitive and composed types - including other defined types. Type definitions are statements (cf. 3.7.21). They are divided into simple type definitions (type aliases), parameterized types and type constructors. A simple type definition looks like:
typedef <a type identifier> = <a type expression>;
The given identifier will be bound to the given type expression. This
new type can henceforth be used in type constraints. The scope of the
type is restricted to all following statements in the block in which
it is defined.
Note, that it is not possible to define a recursive type alias.
Parameterized types look like
|
The effect is similar to type aliases, except that you have to specify
type expressions (cf. above) as arguments if you use the
type. Cf. 3.4.5 for the format of parameters.
Note, that the parameters of the type may be constrained.
Examples for type aliases and parameterized types:
|
Finally, we have type constructors. Type constructors are devices for defining concrete types. They take the following form
|
The first identifier determines the name of the constructed type and
the next one the name of the constructor. The type expression may
contain references to the concrete type (or constructed type), which
means rekursive definitions are possible for constructed types.
It is also possible to use parameters in type constructors:
|
|
The usage of type constructors is described in cf. 3.5.11; for deconstructing type constructors cf. 3.5.15.3.
|
|
|
Reference Manual of LoI - Variables, Parameters and Typing, © 2000 by Gil Müller |
|
|