[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9. Object, Classes and Modules

Kawa provides various ways to define, create, and access Java objects. Here are the currently supported features.

The Kawa module system is based on the features of the Java class system.

9.1 Records  
9.2 Mapping Scheme names to Java names  
9.3 Allocating objects  
9.5 Accessing fields of Java objects  
9.4 Calling Java methods from Scheme  
9.6 Defining new classes  
9.7 Anonymous classes  
9.8 Modules and how they are compiled to classes  

Syntax: this
Returns the "this object" - the current instance of the current class. The current implementation is incomplete, not robust, and not well defined. However, it will have to do for now. Note: "this" is a macro, not a variable, so you have to write it using parentheses: `(this)'. A planned extension will allow an optional class specifier (needed for nested clases).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.1 Records

The Record package provides a facility for users to define their own record data types. Records are extensions of the class Record. These procedures use the Java 1.1 reflection facility.

Function: make-record-type type-name field-names
Returns a record-type descriptor, a value representing a new data type disjoint from all others. The type-name argument must be a string, but is only used for debugging purposes (such as the printed representation of a record of the new type). The field-names argument is a list of symbols naming the fields of a record of the new type. It is an error if the list contains any duplicates.

Function: record-constructor rtd [field-names]
Returns a procedure for constructing new members of the type represented by rtd. The returned procedure accepts exactly as many arguments as there are symbols in the given list, field-names; these are used, in order, as the initial values of those fields in a new record, which is returned by the constructor procedure. The values of any fields not named in that list are unspecified. The field-names argument defaults to the list of field names in the call to make-record-type that created the type represented by rtd; if the field-names argument is provided, it is an error if it contains any duplicates or any symbols not in the default list.

Function: record-predicate rtd
Returns a procedure for testing membership in the type represented by rtd. The returned procedure accepts exactly one argument and returns a true value if the argument is a member of the indicated record type; it returns a false value otherwise.

Function: record-accessor rtd field-name
Returns a procedure for reading the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly one argument which must be a record of the appropriate type; it returns the current value of the field named by the symbol field-name in that record. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd.

Function: record-modifier rtd field-name
Returns a procedure for writing the value of a particular field of a member of the type represented by rtd. The returned procedure accepts exactly two arguments: first, a record of the appropriate type, and second, an arbitrary Scheme value; it modifies the field named by the symbol field-name in that record to contain the given value. The returned value of the modifier procedure is unspecified. The symbol field-name must be a member of the list of field-names in the call to make-record-type that created the type represented by rtd.

Function: record? obj
Returns a true value if obj is a record of any type and a false value otherwise.

Function: record-type-descriptor record
Returns a record-type descriptor representing the type of the given record. That is, for example, if the returned descriptor were passed to record-predicate, the resulting predicate would return a true value when passed the given record.

Function: record-type-name rtd
Returns the type-name associated with the type represented by rtd. The returned value is eqv? to the type-name argument given in the call to make-record-type that created the type represented by rtd.

Function: record-type-field-names rtd
Returns a list of the symbols naming the fields in members of the type represented by rtd. The returned value is equal? to the field-names argument given in the call to make-record-type that created the type represented by rtd.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.2 Mapping Scheme names to Java names

Programs use "names" to refer to various values and procedures. The definition of what is a "name" is different in different programming languages. A name in Scheme (and other Lisp-like languages) can be principle contain any character (if using a suitable quoting convention), but typically names consist of "words" (one or more letters) separated by hyphens, such as `make-temporary-file'. Digits and some special symbols are also used. Standard Scheme is case-insensitive; this means that the names `loop', `Loop', and `LOOP' are all the same name. Kawa is by default case-sensitive, but we recommend that you avoid using upper-case letters as a general rule.

The Java language and the Java virtual machine uses names for classes, variables, fields and methods. These names can contain upper- and lower-case letters, digits, and the special symbols `_' and `$'.

Given a name in a Scheme program, Kawa needs to map that name into a valid Java name. A typical Scheme name such as `make-temporary-file' is not a valid Java name. The convention for Java names is to use "mixed-case" words, such as `makeTemporaryFile'. So Kawa will translate a Scheme-style name into a Java-style name. The basic rule is simple: Hyphens are dropped, and a letter that follows a hyphen is translated to its upper-case (actually "title-case") equivalent. Otherwise, letters are translated as is.

Some special characters are handled specially. A final `?' is replaced by an initial `is', with the following letter converted to titlecase. Thus `number?' is converted to `isNumber' (which fits with Java conventions), and `file-exists?' is converted to `isFileExists' (which doesn't really). The pair `->' is translated to `$To$'. For example `list->string' is translated to `list$To$string'.

Some symbols are mapped to a mnemonic sequence, starting with a dollar-sign, followed by a two-character abbreviation. For example, the less-than symbol `<' is mangled as `$Ls'. See the source code to the mangleName method in the gnu.expr.Compilation class for the full list. Characters that do not have a mnemonic abbreviation are mangled as `$' followed by a four-hex-digit unicode value. For example `Tamil vowel sign ai' is mangled as `$0bc8'.

Note that this mapping may map different Scheme names to the same Java name. For example `string?', `String?', `is-string', `is-String', and `isString' are all mapped to the same Java identifier `isString'. Code that uses such "Java-clashing" names is not supported. There is very partial support for renaming names in the case of a clash, and there may be better support in the future. However, some of the nice features of Kawa depend on being able to map Scheme name to Java names naturally, so we urge you to not write code that "mixes" naming conventions by using (say) the names `open-file' and `openFile' to name two different objects.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.3 Allocating objects

Function: make type args ...
Constructs a new object instance of the specified type, which must be either a java.lang.Class or a <gnu.bytecode.ClassType>.

The args ... are passed to the constructor of the class type. If there is no applicable constructor, and the args ... consist of a set of (keyword,value)-pairs, then the default constructor is called, and each (keyword,value)-pair is used to set the correspdong slot of the result, as if by: (slot-set! result keyword value).

For example, the following are all equivalent:
 
(set! p (make <java.awt.Point> 3 4))

(set! p (make <java.awt.Point> y: 4 x: 3))

(set! p (make <java.awt.Point>))
(slot-set! p 'x 3)
(set! (slot-ref p 'y) 4)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.4 Calling Java methods from Scheme

Kawa has both a low-level and a high-level "Foreign Function Interface", which allows you to call any (virtual or static) Java method as if it were a Scheme procedure.

Function: invoke-static class name args ...
The class can be a <java.lang.Class>, a <gnu.bytecode.ClassType>, or a <symbol> or <string> that names a Java class. The name can be <symbol> or <string> that names one or more methods in the Java class. The name is "mangled" (see section 9.2 Mapping Scheme names to Java names) into a valid Java name.

Any public methods (static or instance) in the specified class (or its super-classes) that match "name" or "name$V" collectively form a generic procedure. When the procedure is applied to the argument list, the most specific applicable method is chosen depending on the argument list; that method is then called with the given arguments. Iff the method is an instance method, the first actual argument is used as the this argument. If there are no applicable methods (or no methods at all!), or there is no "best" method, WrongType is thrown.

("name$V" is used for procedures with #!rest or keyword args; the last argument must be an array type; all the "extra" arguments must be compatible with the type of the array elements.)

An example (derived from the Skij FAQ):
 
(invoke-static <java.lang.Thread> 'sleep 100)

The behavior of interpreted code and compiled code is not indentical, though you should get the same result either way unless you have designed the classes rather strangely. The details will be nailed down later, but the basic idea is that the compiler will "inline" the invoke-static call if it can pick a single "best" matching method.

Function: invoke object name args ...
The name can be <symbol> or <string> that names one or more methods in the Java class. The name is "mangled" (see section 9.2 Mapping Scheme names to Java names) into a valid Java name.

Any public methods (static or instance) in the specified class (or its super-classes) that match "name" or "name$V" collectively form a generic procedure. When the procedure is applied to the argument list, the most specific applicable method is chosen depending on the argument list; that method is then called with the given arguments. Iff the method is an instance method, the object is used as the this argument; otherwise object is prepended to the args list. If there are no applicable methods (or no methods at all!), or there is no "best" method, WrongType is thrown.

("name$V" is used for procedures with #!rest or keyword args; the last argument must be an array type; all the "extra" arguments must be compatible with the type of the array elements.)

The behavior of interpreted code and compiled code is not indentical, though you should get the same result either way unless you have designed the classes rather strangely. The details will be nailed down later, but the basic idea is that the compiler will "inline" the invoke-static call if it can pick a single "best" matching method.

If the compiler cannot determine the method to call (assuming the method name is constant), the compiler has to generate code at run-time to find the correct method. This is much slower, so the compiler will print a warning. To avoid a waning, you can use a type declaration, or insert a cast:
 
(invoke (as <java.util.Date> my-date) 'setDate cur-date)

Function: class-methods class name
Return a generic function containing those methods of class that match the name name, in the sense of invoke-static. Same as:
 
(lambda args (apply invoke-static (cons class (cons name args))))

Some examples using these functions are `vectors.scm' and `characters.scm' the directory `kawa/lib' in the Kawa sources.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.5 Accessing fields of Java objects

Kawa has both a high-level interface and a low-level interface for accessing the fields of Java objects and static fields. The lower-level interfaces are macros that return functions. These functions can be inlined, producing efficient code. The higher-level functions are less verbose and more convenient. However, they can only access public fields.

Function: field object fieldname
Get the instance field with the given fieldname from the given Object. Returns the value of the field, which must be public. This procedure has a setter, and so can be used as the first operand to set!.

The field name is "mangled" (see section 9.2 Mapping Scheme names to Java names) into a valid Java name. If there is no accessible field whose name is "fieldname", we look for a no-argument method whose name is "getFieldname".

If object is a primitive Java array, then fieldname can only be 'length, and the result is the number of elements of the array.

Function: static-field class fieldname
Get the static field with the given fieldname from the given class. Returns the value of the field, which must be public. This procedure has a setter, and so can be used as the first operand to set!.

Examples:
 
(static-field <java.lang.System> 'err)
;; Copy the car field of b into a.
(set! (field a 'car) (field b 'car))

Function: slot-ref object fieldname
A synonym for (field object fieldname).

Function: slot-set! object fieldname value
A synonym for (set! (field object fieldname) value).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.6 Defining new classes

Kawa provides various mechanisms for defining new classes. The define-class and define-simple-class forms will usually be the preferred mechanisms. They have basically the same syntax, but have a couple of differences. define-class allows multiple inheritance as well as true nested (first-class) class objects. However, the implementation is more complex: code using it is slightly slower, and the mapping to Java classes is a little less obvious. (Each Scheme class is implemented as a pair of an interface and an implementation class.) A class defined by define-simple-class is slightly more efficient, and it is easier to access it from Java code.

The syntax of define-class are mostly compatible with that in the Guile and Stk dialects of Scheme.

Syntax: define-class name (supers ...) field-or-method-decl ...
Syntax: define-simple-class name (supers ...) field-or-method-decl ...
 
field-or-method = field-decl | method-decl
field-decl = (fname [:: ftype] [option-keyword option-value]*)
method-decl = ((method-name formal-arguments) [:: rtype] body)
Defines a new class named name. If define-simple-class is used, creates a normal Java class named name in the current package. (If name has the form <xyx> the Java implementation type is named xyz.) If define-class the implementation is unspecified. In most cases, the compiler creates a class pair, consisting of a Java interface and a Java implementation class.

The class inherits from the classes and interfaces listed in supers. This is a list of names of classes that are in scope (perhaps imported using require), or names for existing classes or interfaces surrounded by <>, such as <gnu.lists.Sequence>. If define-simple-class is used, at most one of these may be the name of a normal Java class or classes defined using define-simple-class; the rest must be interfaces or classes defined using define-class. If define-class is used, all of the classes listed in supers should be interfaces or classes defined using define-class.

Each field-decl declares a public instance "slot" (field) with the given fname. If ftype is specified it is the type of the slot. The following option-keywords are implemented:

type: ftype
Specifies that ftype is the type of (the values of) the field. Equivalent to `:: ftype'.
allocation: class:
Specifies that there should be a single slot shared between all instances of the class (and its sub-classes). Not yet implemented for define-class, only for define-simple-class. In Java terms this is a static field.
allocation: instance:
Specifies that each instance has a separate value "slot", and they are not shared. In Java terms, this is a non-static field. This is the default.
init-form: expr
An expression used to initialize the slot. The lexical environment of the expr is that of the define-class or define-simple-class. (This is not quite true in the current implementation, as the names of fields and methods of this class are visible.)
init-value: value
A value expression used to initialize the slot. For now this is synonymous with init-form:, but that may change (depending on what other implementation do), so to be safe only use init-value: with a literal.
init-keyword: name:
A keyword that that can be used to initialize instance in make calls. For now, this is ignored, and name should be the same as the field's fname. static field.

Each method-decl declares a public non-static method, whose name is method-name. (If method-name is not a valid Java method name, it is mapped to something reasonable. For example foo-bar? is mapped to isFooBar.) The types of the method arguments can be specified in the formal-arguments. The return type can be specified by rtype, or is otherwise the type of the body. Currently, the formal-arguments cannot contain optional, rest, or keyword parameters. (The plan is to allow optional parameters, implemented using multiple overloaded methods.)

The scope of the body of a method includes the field-decls of the object. It does include the surrounding lexical scope. It sort-of also includes the declared methods, but this is not working yet.

A simple example:
 
(define-simple-class <2d-vector> ()
  (x type: <double> init-value: 0.0 init-keyword: x:)
  (y type: <double> init-value: 0.0 init-keyword: y:)
  ((add (other :: <2d-vector>)) :: <2d-vector>
   ;; Kawa compiles this using primitive Java types!
   (make <2d-vector>
     x: (+ x (slot-ref other 'x))
     y: (+ y (slot-ref other 'y))))
  ((scale (factor :: <double>)) :: <2d-vector>
   ;; Unfortunately, multiply is not yet optimized as addition is.
   (make <2d-vector> x: (* factor x) y: (* factor y))))

(define-simple-class <3d-vector> (<2d-vector>)
  (z type: <double> init-value: 0.0 init-keyword: z:)
  ((scale (factor :: <double>)) :: <2d-vector>
   ;; Note we cannot override the return type to <3d-vector>
   ;; because Java does not allow that.  Should hide that. .
   (make <3d-vector>
     ;; Unfortunately, slot names of inherited classes are not visible.
     ;; Until this is fixed, use slot-ref.
     x: (* factor (slot-ref (this) 'x))
     y: (* factor (slot-ref (this) 'y))
     z: (* factor z))))


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.7 Anonymous classes

Syntax: object (supers ...) field-or-method-decl ...
Returns a new instance of an anonymous (inner) class. The syntax is similar to define-class.
 
field-or-method = field-decl | method-decl
field-decl = (fname [[[::] ftype] finit])
    | (fname [:: ftype] [option-keyword option-value]*)
method-decl = ((method-name formal-arguments) [[::] rtype] body)

Returns a new instance of a unique (anonymous) class. The class inherits from the list of supers, where at most one of the elements should be the base class being extended from, and the rest are interfaces.

This is roughly equivalent to:
 
(begin
  (define-name hname (supers ...) field-or-method-decl ...)
  (make hname))

A field-decl is as for define-class, except that we also allow an abbreviated syntax. Each field-decl declares a public instance field. If ftype is given, it specifies the type of the field. If finit is given, it is an expression whose value becomes the initial value of the field. The finit is evaluated at the same time as the object expression is evaluated, in a scope where all the fnames are visible.

A method-decl is as for define-class.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.8 Modules and how they are compiled to classes

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.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.8.1 Name visibility

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 for name in the current module, or if it is defined using define-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) ...)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.8.2 Definitions

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 that name is not exported.

Syntax: define-constant name [:: type] value
Definites name to have the given value. 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 a final field with the given name and type. The value is evaluated as normal; however, if it is a compile-time constant, it defaults to being static.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.8.3 How a module becomes a class

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 [not yet implemented] 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.

Syntax: module-name <name>
Sets the name of the generated class, overriding the default. If there is no `.' in the name, 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.Binding object, and the value Scheme variable is defined to be the value stored in the Binding. Howewer, if you specify an explicit type, then the field will have the specified type, instead of being a Binding. The indirection using Binding 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 section 6.4 Compiling Scheme 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 ...
Syntax: module-static #t
Syntax: module-static #f
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, the names 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:

  1. If there is a module-extends or module-implements declaration, then (module-static #f) is implied.
  2. If the --module-static command-line parameter is specified, then (module-static #t) is implied.
  3. (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.)
  4. 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 section 9.2 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.)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

9.8.4 Requiring (importing) a module

You can import a module into the current namespace with require.

Syntax: require modulespec
The modulespec can be either a <classname> or a 'featurename. In either case the names exported by the specified module (class) are added to the current set of visible names.

If modulespec is <classname> where 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 from gnu.expr.ModuleBody or implements java.lang.Runnable then the corresponding run 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 'featurename then the 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.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Per Bothner on November, 26 2001 using texi2html