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.
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).
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.
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.
make-record-type
that created the type represented by rtd.
make-record-type
that created the type represented by rtd.
record-predicate
, the resulting predicate would return a true
value when passed the given record.
eqv?
to the type-name argument given in
the call to make-record-type
that created the type represented by
rtd.
equal?
to the
field-names argument given in the call to make-record-type
that
created the type represented by rtd.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', and if followed by a letter, that is is converted to titlecase. For example `list->string' is translated to `listToString'.
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.
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)
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.
<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 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.
<symbol>
or
<string>
that names one or more methods in the Java class.
The name is "mangled" (see section 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.
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.
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.
setter
, and so can be used as the first
operand to set!
.
The field name is "mangled" (see section 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"
.
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))
(field object fieldname)
.
(set! (field object fieldname) value)
.
field-or-method = field-decl | method-decl field-decl = (field-name [[field-type] field-init]) 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.
Each field-decl declares a public instance field.
If field-type is given, it specifies the type of the field.
If field-init is given, it is an expression whose value
becomes the initial value of the field.
The field-init is evaluated at the same time as the object
expression is evaluated,
in a scope where all the field-names are visible.
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.
(The Kawa module system is under construction. There are undoubtedly bugs, not all of the planned features are implemented yet, and the design may change as we get more experience with it.)
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. (Static modules are not yet implemented.) 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.
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.
-P
Kawa command-line flag) is prepended.
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
.
<class>
.
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 non-private 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
.
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 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.
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. A future extension will support for precise control over which components are static, and will support creation of static modules (all of whose public fields are static). For now, you can only generate instance methods.
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.
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 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
.
<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.
Go to the first, previous, next, last section, table of contents.