Programmers can manage the complexity of large OmniMark scripts by encapsulating parts of the script into modules. A module is a self-contained unit of OmniMark code that specifies which names are visible (or exported) to its importers. Likewise, the importer of a module has control over the names it uses for the things it gets from the module.
By convention, OmniMark modules are placed in files whose names end with the .xmd
suffix, in the
same way that OmniMark programs use .xom
and OmniMark include files use .xin
.
Modules are different from include file in several respects.
Here is a simple module with a function that returns a different (pseudo-random) number each time it's called:
module global integer count initial { 1 } export integer function next () as set count to (count * "ABCDEF97" base 16) shift -1 return count
A module always starts with the keyword module
. module
can be preceded only by
comments and white-space.
After that, things are more or less as they are for any other OmniMark code. You can declare global
s,
constant
s, and function
s, not to mention opaque
and record
types. You can have
rules, including process-start
and process-end
rules.
By default, anyone using (importing) a module doesn't see the names within it: they are hidden. The module
can selectively export
names to make them visible to the importer. In this example, a single name
is exported from the module. This is just to keep the example simple. In practice most modules will export
many names, and hide many others.
Once you have defined a module, you can use it in OmniMark programs and other modules by importing it:
import "mymodule.xmd" unprefixed
The name after the keyword import
is a file name, just as for an include
file. The keyword unprefixed
specifies that all of the names exported by the module
will be used as is.
As an example, if mymodule.xmd is the name of the file containing the pseudo-random number
module described above, then the name next will refer to the next function
exported from the module.
import "mymodule.xmd" unprefixed process repeat to 10 output "10fkd" % next () || "%n" again
Sometimes you will want to use two modules that define the same name, or a module that defines a name used by
your own code. You can specify a prefix when importing a module, to keep the names in different modules separate
from each other and from your own names:
import "mymodule.xmd" prefixed by my. process repeat to 10 output "10fkd" % my.next () || "%n" again
In this case, all the names imported from mymodule.xmd will be referenced as if they had my. prefixed to their names. This means that the my.next will refer to the function next defined in the module.
There are no limitations on what you use as a prefix, so long as the result is a valid name. Remember that OmniMark allows -, . and _ in a name, and these are good things to put at the end of prefixes. You should choose prefixes that help make your code clear and readable.