mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 14:25:56 +00:00
Estimated hours taken: 0.5 compiler/notes/MODULE_SYSTEM Update the specification for the all-singing, all-dancing module system to include a new `module_ops' symbol specifier, after discussions with Peter Schachte.
429 lines
13 KiB
Plaintext
429 lines
13 KiB
Plaintext
%-----------------------------------------------------------------------------%
|
|
|
|
This file contains some very old notes about the module system.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
I've designed my module system. It's based on a Prolog-syntax (but
|
|
not otherwise backwards compatible) strongly-typed logic programming
|
|
language. I'd be interested in any comments people have about it.
|
|
|
|
Note: currently I don't allow nested modules. I want that to change
|
|
eventually, but this will suffice for a first implementation.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
The module system has two primary purposes: to prevent accidental name
|
|
clashes, and to enforce encapsulation. (Although there are other benefits,
|
|
such as encouraging more well-structured software.)
|
|
|
|
Accidental name clashes are prevented by using a standard naming
|
|
convention where every symbol is prefixed by the name of the module in
|
|
which it is defined. This module qualifier may be omitted for symbols
|
|
in the current namespace (i.e. symbols in the current module, or
|
|
symbols which have been explicitly imported into the current namespace)
|
|
so long as no ambiguity results.
|
|
|
|
Encapsulation is enforced by only allowing symbols which have been
|
|
explicitly exported (eg. those in a module's interface section) to be
|
|
used by other modules.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
Each module must consist of
|
|
|
|
:- module ModuleName.
|
|
:- interface.
|
|
...
|
|
:- implementation.
|
|
...
|
|
:- end_module ModuleName. % optional
|
|
|
|
The semantics of these declarations is as follows.
|
|
|
|
:- module ModuleName.
|
|
|
|
Every module must start with a declaration of this form.
|
|
(There is an implementation-dependant mapping from
|
|
module names to filenames which must be adhered to.
|
|
The mapping may be arbitrary complex - for example, the
|
|
implementation may allow the user to list this mapping
|
|
in a configuration file - but in general, the name of the
|
|
file that a module resides in will be just the name
|
|
of the module plus an appropriate extensions.
|
|
The mapping is used by the implementation so that it
|
|
can determine where to find the required modules.)
|
|
|
|
:- use_module Module1, Module2, ..., ModuleN.
|
|
|
|
The exported symbols in the listed modules are made
|
|
available for use in the current module. These symbols must
|
|
still be qualified with a module name, for example
|
|
list:append(A,B,C)
|
|
in order to be used.
|
|
|
|
:- import_module Module1, Module2, ..., ModuleN.
|
|
|
|
As for `use_module', except that symbols are imported into the
|
|
current namespace and do not need to be qualified with a module name
|
|
except to avoid ambiguities.
|
|
(Use of this declaration in large programs is probably a bad idea,
|
|
but it may be useful for small programs. Also, it would probably
|
|
not be a good idea to use this declaration together with low-level
|
|
export directives [described below] that don't specify an
|
|
explicit module qualifier, because it could cause accidental
|
|
re-exporting of imported symbols.)
|
|
|
|
:- export_module Module1, Module2, ..., ModuleN.
|
|
|
|
All exported symbols in the specified modules are re-exported
|
|
from this module. This declaration can only occur in the
|
|
interface section.
|
|
|
|
:- interface.
|
|
All exportable declarations (ie. all type, predicate, constructor, and
|
|
operator declarations) in the interface section
|
|
are automatically exported.
|
|
|
|
:- implementation.
|
|
Declarations and definitions in the implementation section
|
|
are by default private.
|
|
|
|
:- end_module ModuleName.
|
|
|
|
Syntactic sugar.
|
|
This declaration is optional, but if it occurs it must be the
|
|
last declaration in the file and the ModuleName must match with
|
|
that specified in the `module' declaration.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
The module system also has the following lower-level declarations,
|
|
which allow you to import, use, or export individual symbols rather
|
|
than entire modules.
|
|
They can be used instead of (or in conjunction with) the higher-level
|
|
declarations.
|
|
All of the export declarations can only occur in the interface
|
|
section.
|
|
|
|
:- export_sym Sym1, Sym2, ..., SumN.
|
|
Sym1, Sym2, ..., SymN may be any symbol specifiers.
|
|
All symbols matching those symbol specifiers are exported.
|
|
|
|
A symbol specifier is one of
|
|
SymbolNameSpecifier
|
|
Matches any symbol matched by the SymbolNameSpecifier.
|
|
TypedConstructorSpecifier
|
|
Matches any constructors matched by the
|
|
TypedConstructorSpecifier.
|
|
cons(ConstructorSpecifier)
|
|
Matches only constructors.
|
|
pred(PredicateSpecifier)
|
|
Matches only predicates.
|
|
func(FunctionSpecifier)
|
|
Matches only functions.
|
|
adt(SymbolNameSpecifier)
|
|
Matches only type names.
|
|
type(SymbolNameSpecifier)
|
|
Matches type names matched by the SymbolNameSpecifier,
|
|
and also matches any constructors for the matched type
|
|
names.
|
|
op(OpSpecifier)
|
|
Matches any operators matched by OpSpecifier.
|
|
module(ModuleSpecifier)
|
|
Matches all symbols exported from the specified module.
|
|
module_ops(ModuleSpecifier)
|
|
Matches all operators exported from the specified
|
|
module. (This includes any entity exported from
|
|
the module which has the same name as an operator
|
|
declared in the module.)
|
|
|
|
A ConstructorSpecifier is one of
|
|
SymbolSpecifier
|
|
TypedConstructorSpecifier
|
|
|
|
A TypedConstructorSpecifier is one of
|
|
SymbolSpecifier::Type
|
|
Matches only constructors with the specified result
|
|
type.
|
|
SymbolName(ArgType1, ..., ArgTypeN)
|
|
Matches only constructors with the specified argument
|
|
types.
|
|
SymbolName(ArgType1, ..., ArgTypeN)::Type
|
|
Matches only constructors with the specified argument
|
|
and result types.
|
|
|
|
A PredicateSpecifier is one of
|
|
SymbolName(ArgType1, ..., ArgTypeN)
|
|
Matches only predicates with the specified argument
|
|
types.
|
|
SymbolSpecifier
|
|
|
|
A FunctionSpecifier is the same as a ConstructorSpecifier.
|
|
|
|
An OpSpecifier is one of
|
|
SymbolSpecifier
|
|
Matches any operators matched by SymbolSpecifier
|
|
infix(SymbolSpecifier)
|
|
Matches only infix operators
|
|
postfix(SymbolSpecifier)
|
|
Matches only postfix operators
|
|
prefix(SymbolSpecifier)
|
|
Matches only prefix operators
|
|
|
|
A SymbolSpecifier is one of
|
|
SymbolName
|
|
SymbolName/Arity
|
|
Matches only symbols of the specified arity.
|
|
|
|
A SymbolName is one of
|
|
Name
|
|
Matches symbols with the specified name in the
|
|
current namespace.
|
|
ModuleSpecifier:Name
|
|
Matches symbols with the specified name exported
|
|
by the specified module.
|
|
|
|
A ModuleSpecifier is just an identifier.
|
|
|
|
:- export_pred Pred1, ..., PredN.
|
|
This is just an abbreviation for
|
|
:- export_sym pred(Pred1), ..., pred(PredN).
|
|
All predicates matched by the listed PredicateSpecifiers are exported.
|
|
If there is any ambiguity, *all* matching predicates are exported.
|
|
|
|
:- export_func Func1, ..., FuncN.
|
|
This is just an abbreviation for
|
|
:- export_sym func(Func1), ..., func(FuncN).
|
|
All predicates matched by the listed FunctionSpecifiers are exported.
|
|
If there is any ambiguity, *all* matching predicates are exported.
|
|
|
|
:- export_adt Type1, ..., TypeN.
|
|
This is just an abbreviation for
|
|
:- export_sym adt(Type1), ..., adt(TypeN).
|
|
The listed types are exported. Constructors for these types
|
|
are not exported, so this can be used to create abstract data types,
|
|
hence the name. (However this declaration does not prevent
|
|
the constructors from being exported by other declarations.)
|
|
Type1, ..., TypeN can be any SymbolSpecifiers.
|
|
|
|
:- export_cons Cons1, ..., ConsN.
|
|
This is just an abbreviation for
|
|
:- export_sym cons(Cons1), ..., cons(ConsN).
|
|
|
|
:- export_op Op1, ..., OpN.
|
|
This is just an abbreviation for
|
|
:- export_sym op(Op1), ..., op(OpN).
|
|
|
|
:- export_type Type1, ..., TypeN.
|
|
This is just an abbreviation for
|
|
:- export_sym type(Type1), ..., type(TypeN).
|
|
The listed types and all the constructors for the listed types
|
|
are exported.
|
|
|
|
:- export_module_ops
|
|
This is just an abbreviation for
|
|
:- export_sym module_ops(Op1), ..., op(OpN).
|
|
|
|
:- import_module_ops Module1, ..., ModuleN.
|
|
This is just an abbreviation for
|
|
:- export_sym module_ops(Module1), ..., module_ops(ModuleN).
|
|
|
|
:- export_module_ops Module1, Module2, ..., ModuleN.
|
|
All exported operators in the specified modules are re-exported
|
|
from this module. This declaration can only occur in the
|
|
interface section.
|
|
|
|
|
|
:- import_sym Sym1, Sym2, ..., SumN.
|
|
The listed symbols are imported, ie. made available for use
|
|
in the current module.
|
|
Sym1, ..., SymN may be any symbol specifiers, but they
|
|
should have explicit module qualifications.
|
|
|
|
:- import_pred Pred1, Pred2, ..., PredN.
|
|
:- import_func Func1, Func2, ..., FuncN.
|
|
:- import_type Type1, Type2, ..., TypeN.
|
|
:- import_adt Type1, Type2, ..., TypeN.
|
|
:- import_cons Cons1, Cons2, ..., ConsN.
|
|
:- import_op Op1, Op2, ..., OpN.
|
|
Abbreviations for special cases of import_sym.
|
|
|
|
:- use_sym Sym1, Sym2, ..., SumN.
|
|
The listed symbols are imported into the current namespace,
|
|
i.e. made available for use without explicit module
|
|
qualification, exactly as for `use_module'.
|
|
Sym1, ..., SymN may be any symbol specifiers, but they should
|
|
have explicit module qualifications.
|
|
|
|
:- use_pred Pred1, Pred2, ..., PredN.
|
|
:- use_func Func1, Func1, ..., Func1.
|
|
:- use_type Type1, Type2, ..., TypeN.
|
|
:- use_adt Type1, Type2, ..., TypeN.
|
|
:- use_cons Cons1, Cons2, ..., ConsN.
|
|
:- use_op Op1, Op2, ..., OpN.
|
|
Abbreviations for special cases of use_sym.
|
|
|
|
Note that although there are quite a few constructs, most of these
|
|
are syntactic sugar. The first phase of processing fully qualifies
|
|
all symbols, and translates all the module system declarations into
|
|
just `module', `import_sym', and `export_sym'.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
SOME EXAMPLE MODULES
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Version using explicit export declaration.
|
|
|
|
% Bag ADT.
|
|
% Implemented as unsorted lists.
|
|
|
|
:- module bag.
|
|
:- interface.
|
|
:- export_pred init, insert, contains.
|
|
|
|
:- implementation.
|
|
:- import_module list.
|
|
:- type bag(T) = list(T).
|
|
|
|
:- pred init(bag(_)).
|
|
init([]).
|
|
|
|
:- pred insert(bag(T), T, bag(T)).
|
|
insert(Bag0, Item, [Item|Bag0]).
|
|
|
|
:- pred contains(bag(T), T).
|
|
contains(Bag, Item) :-
|
|
list:member(Item, Bag).
|
|
|
|
:- end_module bag.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Version using pred declarations in the interface section.
|
|
|
|
% Bag ADT.
|
|
% Implemented as unsorted lists.
|
|
|
|
:- module bag.
|
|
:- interface.
|
|
|
|
:- pred init(bag(_)).
|
|
:- pred insert(bag(T), T, bag(T)).
|
|
:- pred contains(bag(T), T).
|
|
|
|
:- implementation.
|
|
:- import_module list.
|
|
|
|
:- type bag(T) = list(T).
|
|
|
|
init([]).
|
|
|
|
insert(Bag0, Item, [Item|Bag0]).
|
|
|
|
contains(Bag, Item) :-
|
|
list:member(Item, Bag).
|
|
|
|
:- end_module bag.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Version using type inference to avoid explicit type
|
|
% declarations. (Bad style, IMHO, but we allow for the
|
|
% possibility in order to attract the Prolog die-hards.)
|
|
|
|
% Bag ADT.
|
|
% Implemented as unsorted lists.
|
|
|
|
:- module bag.
|
|
:- import_module list.
|
|
|
|
:- implementation. % ensure that the bag/1 constructor is not exported
|
|
|
|
:- type bag(T) ---> bag(list(T)). % define type bag(T) and
|
|
% constructor bag/1::list(T)->bag(T).
|
|
|
|
:- interface.
|
|
|
|
init(bag([])).
|
|
|
|
insert(bag(Bag0), Item, bag([Item|Bag0])).
|
|
|
|
contains(bag(Bag), Item) :-
|
|
list:member(Item, Bag).
|
|
|
|
:- end_module bag.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Version using type inference to avoid explicit type
|
|
% declarations. (This is speculative; type inference
|
|
% is not high on the list of things to implement.)
|
|
|
|
% Bag ADT.
|
|
% Implemented as unsorted lists.
|
|
|
|
:- module bag.
|
|
:- import_module list.
|
|
:- export_pred init, insert, contains.
|
|
|
|
:- type bag(T) ---> bag(list(T)).
|
|
|
|
init(bag([])).
|
|
|
|
insert(bag(Bag0), Item, bag([Item|Bag0])).
|
|
|
|
contains(bag(Bag), Item) :-
|
|
list:member(Item, Bag).
|
|
|
|
:- end_module bag.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
Here's a rough sketch of an algorithm (probably buggy) for handling these
|
|
things.
|
|
|
|
1. Let S be the set of modules interfaces on which
|
|
the current module (directly or indirectly) depends.
|
|
For each module in S, read in the module and write out the
|
|
full interface of that module (i.e. copy the type and mode
|
|
declarations for the predicates into the interface and add
|
|
`:- adt' declarations for types which are exported as
|
|
opaque abstract data types.
|
|
|
|
2. For each module in S, read in the full interface
|
|
and resolve all the types, insts, and modes in the bodies
|
|
of the symbols defined in the interface (add explicit module
|
|
qualifiers). This gives us a "resolved full interface" file.
|
|
Write this out.
|
|
|
|
3. Read in the resolved full interface files for each module in S.
|
|
Resolve all the types, insts, and modes in the bodies
|
|
of the symbols defined in the current module.
|
|
Resolve all the preds and constructors in the current module.
|
|
Generate code for the current module.
|
|
|
|
/* file t1.nl */
|
|
|
|
:- module t1.
|
|
:- use_module t2.
|
|
:- type t1 ---> g(t2) ; x.
|
|
:- export_type t1/0.
|
|
:- end_module t1.
|
|
|
|
/* full interface file for module t1 */
|
|
:- module t1.
|
|
:- use_module t2.
|
|
:- type t1 ---> g(t2:t2) ; x.
|
|
:- export_type t1/0.
|
|
:- export_adt t2/0.
|
|
:- end_module t1.
|
|
|
|
/* file t2.nl */
|
|
|
|
:- module t2.
|
|
:- use_module t1.
|
|
:- type t2 ---> f(t1:t1) ; y.
|
|
:- export_type t2/0.
|
|
:- end_module t2.
|
|
|
|
%-----------------------------------------------------------------------------%
|