5. Declarations
Variables must be declared before they are used. Aside from a few special cases, declarations have the following formats:
[<qualifier>] [<type>] <identifier> [= <expression>];
A declaration creates a variable with an identifier of
<identifier>, with type <type>, and optionally a
type qualifier of <qualifier>.
Optionally, a declaration may explicitly initialize the value of the new
variable with the value of <expression>.
The two
qualifiers are var and const, which qualify the identifier as mutable
or immutable, respectively. In Gazprea if
the optional qualifier is omitted the default is const, i.e. variables are
immutable by default (see Type Qualifiers).
Both <qualifier> and <type> are optional, but at least one must be
present so that the declaration can be told apart from an assignment. When
<type> is elided it is inferred from <expression>, which must therefore
be present and have an inferable type; if the type cannot be inferred the
compiler must emit a TypeError (see Type Inference and
Errors). When <qualifier> is elided it defaults to const as
described above.
In Gazprea all variables must be initialized in a well-defined manner in
order to ensure functional purity.
Gazprea therefore follows a strict RAII-style discipline: every
declaration is also an initialization, and no
variable is ever observable in an uninitialized state. When the
programmer omits the explicit initializer, the compiler implicitly
initializes the variable to the zero value of its type.
The zero value is 0 for integer, 0.0 for real,
false for boolean, '\0' (the null character) for character, the
empty collection (e.g. the empty string "") for a vector or
string, and, for a fixed-size aggregate type
(array, matrix, tuple, or struct), each element or field set to its own
zero value.
Gazprea has no null value.
An array’s length is likewise settled at initialization and is
then fixed for the remainder of the variable’s lifetime (see
Sizing): an uninitialized array holds its declared
number of elements, each set to the element type’s zero value.
This applies to const declarations as well: a const variable
declared without an initializer is legal and holds the zero value of
its type permanently.
A declaration may appear at any point within a block before its first use. A previous version of the spec placed restrictions such that declarations could only appear at the start of a block, this restriction has been removed as of 2026.
A variable’s name enters scope only after its initializer has been evaluated. A program that refers to a variable within its own initialization statement is therefore ill-formed.
/* All of these declarations are illegal: the right-hand-side identifier
is not yet in scope during its own initializer. */
integer i = i; // assuming that i is not initialized before this point
integer[10] v = v[1] * 2; // likewise v
Errors
This program is ill-formed; the compiler must reject it (SymbolError).
if a variable name is referenced in an initializer but is not defined in the
current scope,
the reference resolves as usual to the nearest enclosing-scope binding of that
name, if one exists. Only when there is no such outer binding is this a
reference
to an undeclared variable, for which the compiler must emit a SymbolError
(see Errors). So integer i = i; at the outermost scope is a
SymbolError, whereas the same text nested inside a scope that already binds
i legally reads the outer i. For instance:
integer x = 7;
if (true) {
integer y = x; /* y gets a value of 7 */
real x = x; /* Refers to the enclosing scope's 'x', so this is legal */
}
/* Now 'x' refers to the integer version, with a value of 7 */
Likewise the following example would be legal, as gazprea allows for variable shadowing:
integer x = 7;
if (true) {
var real x = x; /* x gets a value of 7.0 */
x = x + 1; // x -> std_output would print 8.0
}
/* Now 'x' refers to the integer version, with a value of 7 */
5.1. Special cases
Special cases of declarations are covered in their respective sections.