Modules are function libraries, implemented using basically only two functions:
STD-REQUIRE to load a module and
STD-PROVIDE to provide a module name.
A module name may be a string or a symbol (preferred). If it is a string it may be fully qualified pathname. If so, the module-name is the uppercase basename of the file only.
You may call STD-REQUIRE
in two ways: at RUN-TIME or at LOAD-TIME.
At run-time you put the call into your defun. Because of the efficient implementation it is no serious drawback to call it every time. The advantage is better readability.
At load-time you put it into the lisp file which uses some functions from the required module. Take care that you require it before your lisp calls any of the required function. Take also care to provide the module before you require other modules which require your module! See the example.
Load-time is generally faster than the run-time method, but then you have to take care to avoid too much top-level calls in your module, which require other modules.
A project is a collection of lisp files loaded at once. Therefore we don't need modules if you use projects. Otherwise modules are handy for testing and upgrading parts.
See Projects.
(STD-REQUIRE module-name) loads a module if necessary. This is the actual full definition of std-require, the simple version requires an uppercase string.
This version accepts symbols and strings. How the module-name is stored internally is not defined. Some possibilities:
1. in a global symbol *MODULES* as uppercase string
2. in a global symbol *MODULES* as symbol or
3. using specially prefixed symbols in the atomtable, such as: *STD-MODULE-DEFINED-<module-name>* )
It searches for the following files in exactly this order:
Order of extensions:
<module-name>.LSP
<module-name>.FAS or .BI4 if a those extensions are supported.
<module-name>.ARX if supported.
<module-name>.EXE or .EXP or none dependant on the platform.
It first searches in all paths defined in *MODULE-PATH*
for all the extensions (each path for all extensions), then if none found it tries to load it from the acad library path:
first with (load <module-name>)
, then with (arxload <module-name>)
and at
last with (xload <module-name>)
.
(STD-REQUIRE-PATH module-name filename)
This enables to load modules with different filenames than modulenames, though this should generally not be used to avoid confusion. It's primary usage is to speed up path or extension file-searches. If an explicit path or extension is given in the filename argument then the file is search only there.
(STD-REQUIRE-VERSION module-name version-number)
This also searches for external modules and returns the version number or nil.
(STD-MODULE-DEFINED-P module-name) returns T or Nil
(STD-MODULES) returns a list of all modules as list of symbols.
(STD-MODULE-UNBIND module-name) deletes the module definition to be able to
reload it.
Additionally it may blindly delete all symbols prefixed with "<module-name>-" and
"*<module-name>" and ending "*", such as with the module name "TEST":
TEST-a TEST-x ... and *TEST* *TEST1* ...
This way strict module and symbol names are forced. But deleting symbols is questionable and so far dependent on the global variable *STD:UNBIND-SYMBOL*
Our implementation uses special functions for loading internal modules, namely with STD-%SIMPLE-REQUIRE
and STD-%SIMPLE-PROVIDE
. They assume the modulename as uppercase string. The general STD-REQUIRE
needs functions defined later.
(STD-%SIMPLE-REQUIRE string)
(STD-%SIMPLE-PROVIDE string)
These versions are used internally only to be independent of the yet not loaded STDFILE module and assume uppercase strings and a correct *MODULE-PATH*
with appropriate ending slashes. See below.
When you design a module you must find two names, the long version which names the module and the filename, and a two to four letter abbrevation for the function prefixes.
You must provide a symbol containing the version number as
*
<module-name>-VERSION*
with a numeric value, such as
(setq *TEST-VERSION* 0.1)
The number is needed for (STD-REQUIRE-VERSION)
.
After defining the important functions export the module-name to the system with
(STD-PROVIDE '<module-name>)
, such as
(std-provide 'TEST) ;; or (std-provide "TEST")
Be sure to provide the name BEFORE loading or requiring other modules which require this module! This way desastrous cyclic dependencies are avoided.
See the next paragraph for choosing a good module name:
Module names should have minimal 4 characters and maximal 8 characters for now and their filenames should be uppercase. The provided modulename must match the filename!
Short names may conflict with STD-MODULE-UNBIND which blindly deletes symbols prefixed with "<module-name>-" and "*<module-name>"
Long names may have problems to be loaded on DOS 8.3 filesystems. Long names are truncated on DOS 8.3 filesystems but not on other.
However if the modulename is longer than 8 characters it is better to define it as string, because of the DOS 8.3 filename restriction and to avoid possible problems. For now I found none, but you knows what will happen in future versions. ViLL/Visual Lisp converts filenames to uppercase on storing so you would be on the safe side to use uppercase strings or symbols on long filenames.
If you a provide lowercase string, the module is search for the lowercase filename which might be okay in case-forgiving filesystems but not on unix e.g. (std-modules)
will also return the lowercase string then.
Rules:
Symbols are converted to uppercase strings which are used for finding files with loadable extensions (FAS BI4 LSP ARX ADS EXE)
Strings are not converted to uppercase.
Most used filesystems (FAT based, NTFS with short names) are case-insensitive on searching.
Module filenames should therefore be UPPERCASE and shorter than 8 chars if possible.
For function prefixes you may select then a handy abbrevation of hopefully unique two to four letters. Every function and global variable from your module should then carry this prefix. Good prefixes are abbrevations from the module name, such as COMB-
for the COMBINATIONS module or your name initials, such as DJK-
for David Kozina, esp. for intermediate versions in multi-person modules. (DJK-COMBINATIONS
for the "David Kozina version of the combinations function)
Prefixed with STD-%SIMPLE-
They are needed to load all the internal STDLIB modules, when some extended features are not loaded yet. These versions do not use STDFILE.
Requirements for using STD-SIMPLE- functions used at STDINIT startup:
1) Blindly assuming that pathnames end with a slash.
2) Fielname extensions are prefixed with a dot.
3) Module names must be uppercase strings.
(STD-%SIMPLE-REQUIRE str)
with "STD" module prefix loads only .LSP or .FAS files. External modules must contain the exact extension or be loaded with std-require.
STD-%SIMPLE-LOAD-EXTERNAL
uses the same loading scheme as described in std-require but using only non acad dependant functions to be able to use it from a stand-alone lisp, currently only Vill.exe 2.0, maybe Visual Lisp loaded with an external loader too. (not tested yet)
All STD-% functions are implementation specific hacks and may be replaced by other functions and functionality.
e.g. a global list *MODULES*
of all loaded modules.
The module feature (require, provide) was removed from the X3J13 ANSI Common Lisp standard and replaced with packages which provide their own protected namespace.
However, modules and the require and provide function are still supported with most Lisp, scheme and CL packages. In the scheme standardization modules were also considered too implementation dependent, so not defined in the language, but most scheme systems provide modules support, using require and provide.
It's also similar to perl or python modules, std-require-version is inspired from the perl function require
.