Modules allow for related functions, variables, structs and so forth (bindings) to be grouped into single logical units.
Module declarations are like so:
(module {name} [({attr1} {attr2} ... {attrN})])
Each {attr}
is a module attribute type. The only valid module
attribute is cto
, which is short for 'compile-time only'. If a
module is declared with this attribute, none of its bindings will be
available at runtime.
A given file may only define one module, and modules may not be spread across multiple files (cf. namespaces).
Module files are compiled by using the compiler's -c
flag. The
following files are created on successful compilation, where {name}
is the module name:
-
lib{name}.dtm
- Contains metadata about the module's bindings (functions, variables, structs, etc.).
-
lib{name}.bc
- The LLVM bitcode for the compiled module.
-
lib{name}.o
- The object file for the compiled module.
-
lib{name}.so
- The dynamic library for the compiled module.
-
lib{name}-nomacros.bc
,lib{name}-nomacros.o
,lib{name}-nomacros.so
- As per the corresponding
lib{name}
files, except that all of the macros have been removed.
- As per the corresponding
Bindings within a module that have intern
linkage will not be
available outside of the module.
Dale does not make use of the object files (e.g. lib{name}.o
) when
importing a module. They are produced in case they are needed in a
linking process managed separately from Dale: for example, when
statically linking a module's functions into a C or C++ program.
To use a module:
(import {module-name} [({binding1} {binding2} ... {bindingN})])
By default, all extern
-scope bindings within a module, including
bindings imported by the module itself, will be made available within
the importing file. If {binding}
instructions are included, only
those bindings will be imported. A {binding}
instruction may
reference an entire namespace, in which case all bindings within that
namespace will be imported.
Linking is handled automatically when a module is imported. Whether a particular module is linked statically or dynamically depends on the arguments passed to the compiler at the command line. However, if the module depends on a non-Dale external library, that library must be linked against manually.
The include
core form is supported for completeness' sake. It
operates in the same way as an #include
directive in C's
preprocessor. It may be used both at the top level and within a
procedure.
To prevent a file from being included more than once, the core form
once
can be used as the first form within the file. It takes an
identifier as its single argument. If a file is subsequently loaded by
way of include
, and that file's first form is a call to once
with
the same identifier, the file's remaining forms will be ignored.
Module compilation and use depends on the system permitting lazy
loading of dynamic libraries. If the system doesn't permit this, then
compilation will fail when a module M1 depends on a module M2, and M2
depends on a non-Dale library that isn't passed in via the -a
(make
library available at compile-time) option.
NixOS is an example of a system that does not permit lazy loading by
default, and is affected by this problem. In the NixOS case, this can
be worked around by way of the hardeningDisable=bindnow
option. See
mawled/issues/12 for an
example case.