[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The Q language allows you to declare function and (free) variable
symbols explicitly, by means of the syntactic constructs discussed in
this chapter. Symbol declarations are mostly optional; if you introduce
a new symbol without declaring it, the compiler declares it for
you. However, you will sometimes wish to ensure that a new symbol is
created to override a symbol of the same name from an imported module,
or you want to attach special attributes to a symbol, and then you have
to use an explicit declaration. Explicit declarations are also needed to
introduce new operator and type symbols. Moreover, while implicit
declarations are often convenient when you want to get something done
quickly, for larger projects you might prefer to play it safe and ensure
that each function symbol actually has a proper explicit declaration. In
such cases you can run the compiler with the --pedantic
option
(see Running Compiler and Interpreter) to warn you about
undeclared occurrences of an identifier.
5.1 Declaration Syntax | ||
5.2 Operator Declarations | ||
5.3 Cross-Checking of Declarations and Aliases | ||
5.4 Type Declarations |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Syntactically, symbol declarations take the following form:
declaration : prefix headers ';' | [scope] 'type' unqualified-identifier [':' identifier] ['=' sections] ';' | [scope] 'extern' 'type' unqualified-identifier [':' identifier] ['=' sections] ';' | [scope] 'type' qualified-identifier ['as' unqualified-identifier] ';' | [scope] 'type' unqualified-identifier '==' identifier ';' prefix : scope | [scope] modifier {modifier} scope : 'private'|'public' modifier : 'const'|'special'|'extern'|'var'|'virtual' headers : header {',' header} header : unqualified-identifier '=' expression | unqualified-identifier {['~'] variable-identifier} | qualified-identifier {['~'] variable-identifier} ['as' unqualified-identifier] | '(' unqualified-opsym ')' {['~'] variable-identifier} ['@' precedence] | '(' qualified-opsym ')' {['~'] variable-identifier} ['@' precedence] ['as' unqualified-opsym] precedence : unsigned-number | '(' operator ')' sections : section {'|' section} section : [prefix] headers |
For instance, the following are all valid symbol declarations:
public foo; private extern bar X; public special lambda X Y; special cond::ifelse ~P X Y as myifelse; public (--) Xs Ys; private (::++) Xs Ys as concat; const red, green, blue; var FOO, BAR; var const e = exp 1; type Day = const sun, mon, tue, wed, thu, fri, sat; public type BinTree = virtual bintree Xs | private const nil, bin X T1 T2; |
The keywords private
and public
specify the scope of
a symbol. Public symbols are accessible outside a script, and can
be imported by other modules (see Scripts and Modules). If the
keywords private
and public
are omitted, the scope
defaults to private.
The special
keyword serves to declare special forms, which
are described in Special Forms. Function symbols declared with
const
introduce "constant" or "constructor" symbols; the
compiler enforces that expressions created with such symbols are not
redefined in an equation (cf. Equations). The built-in constants
true
, false
, []
and ()
are already
predeclared as const
. The virtual
keyword serves to
declare "virtual constructors" which can be used to define concrete
representations (so-called "views") of abstract data types, see
Views. Non-const
function symbols can also be declared as
extern
, meaning that the corresponding function is actually
implemented by a corresponding "external" module, see C Language Interface.
The var
keyword allows you to declare "free" variable symbols,
which can be assigned a value by means of a variable definition,
see Free Variables. If you use such a declaration, the variable
symbol may also start with a lowercase letter (if it has not already
been declared as a function symbol); note that without such a
declaration, an identifier starting with a lowercase letter will
implicitly be declared as a function symbol. (The idea behind this is
that you can make a variable look like a function symbol, if the
variable is actually supposed to be used for function values.)
Variable symbols can also be declared as const
; in this case the
variable can only be assigned to once and always refers to the
same value once it has been defined. The built-in variables
INPUT
, OUTPUT
, ERROR
and ARGS
are
predeclared as const
, see Command Language. A variable
declaration may also contain an "initializer" of the form = X
,
where X
is an expression. This has the effect of declaring and
defining the variable in a single step.
As indicated, the scope-modifier prefix of a declaration is followed by
a comma-separated list of headers. The first identifier in each
header states the symbol to be declared. In function symbol declarations
the function identifier may be followed by a list of variable
identifiers which are used to denote the arguments of the declared
function symbol. The variables are effectively treated as comments; only
their number (called the arity of a function symbol) is recorded
to check the consistency of different declarations of the same symbol,
and to specify the number of "special" arguments in a special
declaration. In special
declarations variables may also be
prefixed with `~' to declare them as "non-special" arguments; see
Special Forms.
The first declaration of an unqualified identifier or operator symbol in
a module always introduces a new symbol in the module's namespace. By
default (if no explicit declaration precedes the first use of a new,
unqualified symbol), the compiler automatically declares it as a
private
nullary function or variable symbol, depending on the
case of the initial letter of the identifier (as already mentioned in
Lexical Matters, capitalized identifiers are interpreted as
variable symbols, others as function symbols).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As of Q 6.2, it is also possible to declare new prefix and infix operator symbols. Such declarations are never optional; operator symbols must always be declared before they are used. Syntactically, they take exactly the same form as a function symbol declaration, but the operator symbol is given inside parentheses. As discussed in Lexical Matters, the operator symbol may either be a function identifier or a sequence of punctuation characters. Examples:
public (myop) X Y; public (--) X Y; |
You can also specify a precedence level, like so:
public (myop) X Y @ 2; // use explicit precedence level ... public (myop) X Y @ (<); // ... or precedence level of given operator |
If no precedence is given, it defaults to 2, which is the precedence of the relational operators. This is described in more detail in User-Defined Operators.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The Q language allows multiple declarations of the same symbol, and the
compiler will verify the consistency of all declarations of a given
symbol. That is, if a symbol is declared with different scope
(private
or public
), attributes (like var
,
const
or special
) or different number of arguments, or if
non-special arguments of a special form are declared differently, then
the compiler will issue an error message.
Note, however, that the compiler will never cross-check the number of parameters in a function symbol declaration with the actual number of arguments to which the function is applied when it is used in an equation or a variable definition. This is because in Q it is perfectly legal to have a function symbol applied to a varying number of arguments for the purpose of constructing partial applications. Also note that if a local function symbol is first used without an explicit declaration then it will be implicitly declared to have a zero argument count. If the symbol is then later declared with a nonzero number of arguments or other non-default attributes then the compiler will print an error message.
As indicated by the syntactic rules, it is also possible to redeclare a qualified symbol. This requires that the target module either is the current module or has already been imported in the current scope using an unqualified import clause, and causes the compiler to both cross-check the declaration with the declaration in the imported module and redeclare the symbol within the current namespace.
Such a redeclaration serves several different purposes. First and
foremost, it allows you to ensure that an imported symbol was actually
declared with the given attributes. Second, it lets you resolve name
clashes by redeclaring the symbol in the current scope where it
overrides unqualified imports of other modules; if you want, you can
also import the symbol under a new name using an `as' clause (the
new symbol is then also referred to as an alias of the original
one). In these two cases the symbol will normally be redeclared as a
private symbol. Third, by redeclaring a symbol as public
, you
cause the symbol to be reexported by the current module. Examples:
import gnats; private gnats::foo X Y; // cross-check declaration of gnats::foo public gnats::foo X Y as bar; // reexport gnats::foo as bar |
Of course, the same also works with operator symbols. E.g., here is how
you declare a new operator symbol concat
which behaves exactly
like the built-in `++' operator:
public (::++) X Y as concat; |
There is yet another usage of an imported symbol redeclaration, namely the
extern
redeclaration. This is only possible with function symbols
and if the redeclared symbol is not already declared extern
by
another module. It instructs the compiler that this module provides an
external definition of the symbol which will override equational
definitions in other modules, see C Language Interface, for
details.
Note that, as of Q 7.8, most of the uses of qualified symbol redeclarations are now covered in a more convenient fashion by qualified imports, see Qualified Imports. Thus qualified symbol redeclarations will now mostly be used for cross-checking declarations and for external overrides.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A type declaration introduces a type identifier, an optional
supertype for the type, and an optional list of "constructor" symbols
for the type. It associates the function symbols in the list with the
given type. The symbol list is a |
-delimited list of individual
sections. Each section takes the form of an ordinary symbol
declaration, consisting of scope/modifier prefix and a comma-delimited
list of headers. The prefix is optional; by default, a symbol has the
same scope as the type it belongs to. To change scope and attributes of
a symbol on the list, you use an appropriate scope/modifier prefix. For
instance, you can declare a public BinTree
type with private
constructors nil
and bin
as follows:
public type BinTree = private const nil, bin X T1 T2; |
Each constructor symbol may only be declared once; otherwise the compiler will issue an error message. Hence the compiler enforces that a constructor symbol only belongs to a single type, and that the number of arguments of that symbol is determined uniquely.
A constructor symbol may also be declared as virtual
, which
indicates that it may be used in concrete representations of abstract
data types, so-called "views". Such symbols are usually
non-const
symbols implementing a public construction function
which delivers values of the type they belong to, see Views, for
details. For instance, to add a virtual constructor bintree
to
the above BinTree
type, you might declare the type as follows:
public type BinTree = virtual bintree Xs | private const nil, bin X T1 T2; |
Type identifiers may begin with either an upper- or lowercase letter (the convention, however, is to use capitalized identifiers). There may only be a single declaration for each type. As of Q 7.8, type identifiers must also be distinct from function or variable symbols declared in the same namespace, as both type and function/variable identifiers may now occur in the same context (namely, a qualified import clause).
Types are used on the left-hand side of equations to restrict the set of
expressions which can be matched by a variable; see Type Guards,
for details. The Q language has a number of predefined type symbols
which distinguish the corresponding types of objects built into the
language: Bool
, Int
, Float
, Real
(which is
the supertype of both Int
and Float
), Num
(the
supertype of Real
), String
, Char
(the subtype of
String
denoting the single-character strings), File
,
List
, Stream
, Tuple
and Function
. (Besides
this, there are also two built-in types for exceptions, see
Exception Handling.)
Like function symbols, types imported from other modules can also be redeclared (possibly under a new name), and reexported, e.g.:
public type array::Array as MyArray; |
In this case, no constructor symbols or supertype are specified. (As of Q 7.8, it is also possible to use a qualified import clause to achieve this, see Qualified Imports. Also note that if a type is imported using a qualified import clause, then its constructor symbols will not be imported automatically; you will have to explicitly list them in the import clause if you need them.)
As of Q 7.1, there is an alternative, more customary syntax for introducing type aliases which looks as follows:
type Integer == Int; |
Note that here the alias to be defined is specified first, and the existing type to be aliased can be specified without a module qualifier. This works exactly like an ordinary alias declaration, but resembles more closely the syntax commonly used for defining type synonyms in other languages like Pascal and Haskell.
Types can also be declared as extern
, to indicate that they are
realized in a corresponding C module, as described in C Language Interface. External types may have a supertype, but only virtual
constructors. For instance:
extern type Bar = virtual bar X; |
In difference to function symbols, an existing type imported from
another module cannot be redeclared as extern
. Therefore an
external definition must always be given by the module which originally
declares the type.
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Albert Gräf on February, 23 2008 using texi2html 1.76.