A module is a set of definitions that the module exports, as well as some actions (expressions evaluated for their side effect). The top-level forms in a Scheme source file compile a module; the source file is the module source. When Kawa compiles the module source, the result is the module class. Each exported definition is translated to a public field in the module class.
There are two kinds of module class: A static module is a class (or gets compiled to a class) all of whose public fields a static, and that does not have a public constructor. A JVM can only have a single global instance of a static module. An instance module has a public default constructor, and usually has at least one non-static public field. There can be multiple instances of an instance module; each instance is called a module instance. However, only a single instance of a module can be registered in an environment, so in most cases there is only a single instance of instance modules. Registering an instance in an environment means creating a binding mapping a magic name (derived from the class name) to the instance.
In fact, any Java class class that has the properties of either an instance module or a static module, is a module, and can be loaded or imported as such; the class need not have written using Scheme.
The definitions that a module exports are accessible to other modules.
These are the "public" definitions, to use Java terminology.
By default, all the identifiers declared at the top-level of a module
are exported, except those defined using define-private
.
However, a major purpose of using modules is to control the set of
names exported. One reason is to reduce the chance of accidental
name conflicts between separately developed modules. An even more
important reason is to enforce an interface: Client modules should
only use the names that are part of a documented interface, and should
not use internal implementation procedures (since those may change).
If there is a module-export
declaration in the module, then
only those names listed in a module-export
are exported.
There can be more than one module-export
, and they can be
anywhere in the Scheme file. As a matter of good style, I recommend
a single module-export
near the beginning of the file.
Syntax: module-export
name
...
Make the definition for each
name
be exported. Note that it is an error if there is no definition forname
in the current module, or if it is defined usingdefine-private
.
In this module, fact
is public and worker
is private:
(module-export fact) (define (worker x) ...) (define (fact x) ...)
Alternatively, you can write:
(define-private (worker x) ...) (define (fact x) ...)
In addition to define
(which can take an optional type specifier),
Kawa has some extra definition forms.
Syntax: define-private
name
[::
type
] value
Syntax: define-private
(name
formals
) body
Same as
define
, except thatname
is not exported.
Syntax: define-constant
name
[::
type
] value
Definites
name
to have the givenvalue
. The value is readonly, and you cannot assign to it. (This is not fully enforced.) If the definition is at module level, then the compiler will create afinal
field with the given name and type. Thevalue
is evaluated as normal; however, if it is a compile-time constant, it defaults to being static.
Syntax: define-variable
name
[init
]
If
init
is specified andname
does not have a global variable binding, theninit
is evaluated, andname
bound to the result. Otherwise, the value bound toname
does not change. (Note thatinit
is not evaluated ifname
does have a global variable binding.)Also, declares to the compiler that
name
will be looked up in the dynamic environment. This can be useful for shutting up warnings from--warn-undefined-variable
.This is similar to the Common Lisp
defvar
form. However, the Kawa version is (currently) only allowed at module level.
Syntax: define-namespace
name
namespace-uri
Defines
name
as namespace alias - a lexically scoped "nickname" for the namespace (or package) whose full name isnamespace-uri
, which should be a string literal. Any symbols in the scope of this definitions that contain a colon, and where the part before the colon matches thename
will be treated as being in the package/namespace whose global unique name is thenamespace-uri
.(A namespace is similar to a Common Lisp package, and the
namespace-uri
is like the name of the package. However, a namespace alias belongs to the lexical scope, while a Common Lisp package nickname is global and belongs to the package itself.)The features is currently used for XML (to be documented). In XML terminology, a name containing a colon is a qualified name. The part of the name before the colon is a namespace prefix, which is an aliases for a locally-visible namespace uri. The latter is an arbitrary string, but for uniqueness it is recommended that it be a uri belonging to the organization that defines the namespace. (It need to correspond to an actual browsable location, even though it looks like one.) The part of a name following the colon is the
local part
of the name.If the namespace starts with the strings
"class:"
, then thename
can be used for invoking Java methods (see Calling Java methods from Scheme) and accessing fields (see Accessing fields of Java objects).Instead of a
vamespace-uri
you can use a variable that names a class, usually of the form<
. The following are equivalent:classname
>(define-namespace sb "class:java.lang.StringBuffer") (define-namespace sb <java.lang.StringBuffer>)However, there is one important difference: The
<
is first searched in the lexical scope. It may resolve to a class defined in the current compilation unit (perhaps defined usingclassname
>define-simple-class
), or imported from another module, or an alias (such as fromdefine-alias
). Only if<
is not found in the current scope is it tried as the class nameclassname
>classname
.
If you want to just use a Scheme module as a module (i.e. load
or require
it), you don't care how it gets translated
into a module class. However, Kawa gives you some control over how this
is done, and you can use a Scheme module to define a class which
you can use with other Java classes. This style of class definition
is an alternative to define-class
,
which lets you define classes and instances fairly conveniently.
The default name of the module class is the main part of the
filename of the Scheme source file (with directories and extensions
sripped off). That can be overridden by the -T
Kawa
command-line flag. The package-prefix specified by the -P
flag is prepended to give the fully-qualified class name.
Sets the name of the generated class, overriding the default. If there is no ‘
.
’ in thename
, the package-prefix (specified by the-P
Kawa command-line flag) is prepended.
By default, the base class of the generated module class is unspecified;
you cannot count on it being more specific than Object
.
However, you can override it with module-extends
.
Syntax: module-extends
<class>
Specifies that the class generated from the immediately surrounding module should extend (be a sub-class of) the class
<
.class
>
Syntax: module-implements
<interface>
...
Specifies that the class generated from the immediately surrounding module should implement the interfaces listed.
Note that the compiler does not currently check that all the abstract methods requires by the base class or implemented interfaces are actually provided, and have the correct signatures. This will hopefully be fixed, but for now, if you are forgot a method, you will probably get a verifier error
For each top-level exported definition the compiler creates a
corresponding public field with a similar (mangled) name.
By default, there is some indirection: The value of the Scheme variable
is not that of the field itself. Instead, the field is a
gnu.mapping.Symbol
object, and the value Scheme variable is
defined to be the value stored in the Symbol
.
Howewer, if you specify an explicit type, then the field will
have the specified type, instead of being a Symbol
.
The indirection using Symbol
is also avoided if you use
define-constant
.
If the Scheme definition defines a procedure (which is not re-assigned
in the module), then the compiler assumes the variable as bound as a
constant procedure. The compiler generates one or more methods
corresponding to the body of the Scheme procedure. It also generates
a public field with the same name; the value of the field is an
instance of a subclass of <gnu.mapping.Procedure>
which when
applied will execute the correct method (depending on the actual arguments).
The field is used when the procedure used as a value (such as being passed
as an argument to map
), but when the compiler is able to do so,
it will generate code to call the correct method directly.
You can control the signature of the generated method by declaring
the parameter types and the return type of the method. See the
applet (see Compiling to an applet) example for how this can be done.
If the procedures has optional parameters, then the compiler will
generate multiple methods, one for each argument list length.
(In rare cases the default expression may be such that this is
not possible, in which case an "variable argument list" method
is generated instead. This only happens when there is a nested
scope inside the default expression, which is very contrived.)
If there are #!keyword
or #!rest
arguments, the compiler
generate a "variable argument list" method. This is a method whose
last parameter is either an array or a <list>
, and whose
name has $V
appended to indicate the last parameter is a list.
Top-leval macros (defined using either define-syntax
or defmacro
) create a field whose type is currently a sub-class of
kawa.lang.Syntax
; this allows importing modules to detect
that the field is a macro and apply the macro at compile time.
Syntax: module-static
name
...
Control whether the generated fields and methods are static. If
#t
is specified, then the module will be a static module, all definitions will be static, and the module body is evaluated in the class's static initializer. Otherwise, the module is an instance module. However, thename
s that are explicitly listed will be compiled to static fields and methods. If#f
is specified, then all exported names will be compiled to non-static (instance) fields and methods.By default, if no
module-static
is specified, the following rules apply:
If there is a
module-extends
ormodule-implements
declaration, then(module-static #f)
is implied.If the
--module-static
command-line parameter is specified, then(module-static #t)
is implied.(Not yet implemented: If there are no top-level actions and all definitions are procedure definitions, macro definitions, or constant definitions, then
(module-static #t)
is implied.)Otherwise, a method will be static iff it doesn't need to reference non-static fields or methods of the module instance. In that case, the corresponding field will also be static.
Note
(module-static #t)
usually produces more efficient code, and is recommended if a module contains only procedure or macro definitions. (This may become the default.) However, a static module means that all environments in a JVM share the same bindings, which you may not want if you use multiple top-level environments.
Unfortuntely, the Java class verifier does not allow fields to have
arbitrary names. Therefore, the name of a field that represents a
Scheme variable is "mangled" (see Mapping Scheme names to Java names) into an acceptable Java name.
The implementation can recover the original name of a field X
as ((gnu.mapping.Named) X).getName()
because all the standard
compiler-generate field types implemented the Named
interface.
The top-level actions of a module will get compiled to a run
method. If there is an explicit method-extends
, then the
module class will also automatically implement java.lang.Runnable
.
(Otherwise, the class does not implement Runnable
, since in that
case the run
method return an Object
rather than void
.
This will likely change.)
You can import a module into the current namespace with require
.
The
modulespec
can be either a<
or aclassname
>'
. In either case the names exported by the specified module (class) are added to the current set of visible names.featurename
If
modulespec
is<
whereclassname
>classname
is an instance module (it has a public default constructor), and if no module instance for that class has been registered in the current environment, then a new instance is created and registered (using a "magic" identifier). If the module class either inherits fromgnu.expr.ModuleBody
or implementsjava.lang.Runnable
then the correspondingrun
method is executed. (This is done after the instance is registered so that cycles can be handled.) These actions (creating, registering, and running the module instance) are done both at compile time and at run time, if necessary.All the public fields of the module class are then incorporated in the current set of local visible names in the current module. This is done at compile time - no new bindings are created at run-time (except for the magic binding used to register the module instance), and the imported bindings are private to the current module. References to the imported bindings will be compiled as field references, using the module instance (except for static fields).
If the
modulespec
is'
then thefeaturename
featurename
is looked up (at compile time) in the "feature table" which yields the implementing<
.classname
>
For some examples, you may want to look in the gnu/kawa/slib
directory.