The Kawa Scheme Language

The Kawa Scheme Language

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.

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).

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.

define-class class-name (supers ...) (annotation|option-pair)* field-or-method-decl ...

define-simple-class class-name (supers ...) (annotation|option-pair)* field-or-method-decl ...

Defines a new class named class-name. If define-simple-class is used, creates a normal Java class named class-name in the current package. (If class-name has the form <xyz> the Java implementation type is named xyz.) For 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.

class-name ::= identifier
option-pair ::= option-keyword option-value
field-or-method-decl ::= field-decl | method-decl

General class properties

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 optionally 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.

interface: make-interface

Specifies whether Kawa generates a Java class, interface, or both. If make-interface is #t, then a Java interface is generated. In that case all the supertypes must be interfaces, and all the declared methods must be abstract. If make-interface is #f, then a Java class is generated. If interface: is unspecified, the default is #f for define-simple-class. For define-class the default is to generate an interface, and in addition (if needed) a helper class that implements the interface. (In that case any non-abstract methods are compiled to static methods. The methods that implement the interface are just wrapper methods that call the real static methods. This allows Kawa to implement true multiple inheritance.)

access: kind

Specifies the Java access permission on the class. Can be one of 'public (which is the default in Kawa), 'package (which the default "unnamed" permission in Java code), 'protected, 'private, 'volatile, or 'transient. Can also be used to specify final, abstract, or enum, as in Java. (You don't need to explicitly specify the class is abstract if any method-body is #!abstract, or you specify interface: #t.) The kind can also be a list, as for example:

access: '(protected volatile)
class-name: "cname"

Specifies the Java name of the created class. The name specified after define-class or define-simple-class is the Scheme name, i.e. the name of a Scheme variable that is bound to the class. The Java name is by default derived from the Scheme name, but you can override the default with a class-name: specifier. If the cname has no periods, then it is a name in the package of the main (module) class. If the cname starts with a period, then you get a class nested within the module class. In this case the actual class name is moduleClass$rname, where rname is cname without the initial period. To force a class in the top-level (unnamed) package (something not recommended) write a period at the end of the cname.

Declaring fields

field-decl ::= (field-name (annotation | opt-type-specifier | field-option)*)
field-name ::= identifier
field-option ::= keyword expression

As a matter of style the following order is suggested, though this not enforced:

Each field-decl declares a instance "slot" (field) with the given field-name. By default it is publicly visible, but you can specify a different visiblity with the access: specifier. The following field-option keywords are implemented:

type: type

Specifies that type is the type of (the values of) the field. Equivalent to ‘:: type’.

allocation: kind

If kind is 'class or 'static a single slot is 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.

If kind is 'instance then 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.

access: kind

Specifies the Java access permission on the field. Can be one of 'private, 'protected, 'public (which is the default in Kawa), or 'package (which the default "unnamed" permission in Java code). Can also be used to specify volatile, transient, enum, or final, as in Java.

init: expr

An expression used to initialize the slot. The expression is evaluated in a scope that includes the field and method names of the current class.

init-form: expr

An expression used to initialize the slot. The lexical environment of the expr is that of the define-class; it does not include the field and method names of the current class. or define-simple-class.

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 field-name.

The field-name can be left out. That indicates a "dummy slot", which is useful for initialization not tied to a specific field. In Java terms this is an instance or static initializer, i.e., a block of code executed when a new instance is created or the class is loaded.

In this example, x is the only actual field. It is first initialized to 10, but if (some-condition) is true then its value is doubled.

(define-simple-class <my-class> ()
  (allocation: 'class
   init: (perform-actions-when-the-class-is-initizalized))
  (x init: 10)
  (init: (if (some-condition) (set! x (* x 2)))))

Declaring methods

method-decl ::= ((method-name formal-arguments)
    (annotation | opt-return-type | option-pair)* [deprecated-return-specifiermethod-body)
method-name ::= identifier
method-body ::= body | #!abstract | #!native
deprecated-return-specifier ::= identifier

Each method-decl declares a method, which is by default public and non-static, and 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 a opt-return-type, deprecated-return-specifier, 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.)

A method-decl in a define-simple-class can have the following option-keywords:

access: kind

Specifies the Java access permission on the method. Can be one of 'private, 'protected, 'public, or 'package.

allocation: kind

If kind is 'class or 'static creates a static method.

throws: ( exception-class-name ... )

Specifies a list of checked exception that the method may throw. Equivalent to a throws specification in Java code. For example:

(define-simple-class T
  (prefix)
  ((lookup name) throws: (java.io.FileNotFoundException)
   (make java.io.FileReader (string-append prefix name))))

The scope of the body of a method includes the field-decls and field-decls of the body, including those inherited from superclasses and implemented interfaces.

If the method-body is the special form #!abstract, then the method is abstract. This means the method must be overridden in a subclass, and you're not allowed to create an instance of the enclosing class.

(define-simple-class Searchable () interface: #t
  ((search value) :: boolean #!abstract))

If the method-body is the special form #!native, then the method is native, implemented using JNI.

The special method-name*init*’ can be used to name a non-default constructor (only if make-interface discussed above is #f). It can be used to initialize a freshly-allocated instance using passed-in parameters. You can call a superclass or a sibling constructor using the invoke-special special function. (This is general but admittedly a bit verbose; a more compact form may be added in the future.) See the example below.

Example

In the following example we define a simple class 2d-vector and a class 3d-vector that extends it. (This is for illustration only - defining 3-dimensional points as an extension of 2-dimensional points does not really make sense.)

(define-simple-class 2d-vector ()
  (x ::double init-keyword: x:)
  ;; Alternative type-specification syntax.
  (y type: double init-keyword: y:)
  (zero-2d :: 2d-vector allocation: 'static
   init-value: (2d-vector 0))
  ;; An object initializer (constructor) method.
  ((*init* (x0 ::double) (y0 ::double))
   (set! x x0)
   (set! y y0))
  ((*init* (xy0 ::double))
   ;; Call above 2-argument constructor.
   (invoke-special 2d-vector (this) '*init* xy0 xy0))
  ;; Need a default constructor as well.
  ((*init*) #!void)
  ((add (other ::2d-vector)) ::2d-vector
   ;; Kawa compiles this using primitive Java types!
   (2d-vector
     x: (+ x other:x)
     y: (+ y other:y)))
  ((scale (factor ::double)) ::2d-vector
   (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:)
  ;; A constructor which calls the superclass constructor.
  ((*init* (x0 ::double) (y0 ::double) (z0 ::double))
   (invoke-special 2d-vector (this) '*init* x0 y0)
   (set! z z0))
  ;; Need a default constructor.
  ((*init*) #!void)
  ((scale (factor ::double)) ::2d-vector
   ;; Note we cannot override the return type to 3d-vector
   ;; because Kawa doesn't yet support covariant return types.
   (3d-vector
     x: (* factor x)
     y: (* factor (this):y) ;; Alternative syntax.
     z: (* factor z))))

Note we define both explicit non-default constructor methods, and we associate fields with keywords, so they can be named when allocating an object. Using keywords requires a default constructor, and since having non-default constructors suppresses the implicit default constructor we have to explicitly define it. Using both styles of constructors is rather redundant, though.

Synchronized methods

Kawa doesn't directly support marking a method as synchronized, but you can get the same effect using a synchronized expression:

(define-simple-class <Bar> ()
  ;; non-static method
  ((foo) :: void
   (synchronized (this)
		 (synchronized-block)))
  ;; static method
  ((baz) allocation: 'static :: void
   (synchronized <Bar>
		 (synchronized-block))))

Anonymous classes

object (supers ...) field-or-method-decl ...

Returns a new instance of an anonymous (inner) class. The syntax is similar to define-class.

object-field-or-method-decl ::= object-field-decl | method-decl
object-field-decl ::= (field-name (annotation | opt-type-specifier | field-option)*  [object-init)
object-init ::= expression

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-simple-class 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 object-finit is given, it is an expression whose value becomes the initial value of the field. The object-init is evaluated at the same time as the object expression is evaluated, in a scope where all the field-names are visible.

A method-decl is as for define-class.

Lambda as shorthand for anonymous class

An anonymous class is commonly used in the Java platform where a function language would use a lambda expression. Examples are call-back handlers, events handlers, and run methods. In these cases Kawa lets you use a lambda expression as a short-hand for an anonymous class. For example:

(button:addActionListener
  (lambda (e) (do-something)))

is equivalent to:

(button:addActionListener
  (object (java.awt.event.ActionListener)
    ((actionPerformed (e ::java.awt.event.ActionEvent))::void
     (do-something))))

This is possible when the required type is an interface or abstract class with a Single (exactly one) Abstract Methods. Such a class is sometypes called a SAM-type, and the conversion from a lambda expression to an anonymous class is sometimes called SAM-conversion.

Note that Kawa can also infer the parameter and return types of a method that overrides a method in a super-class.

Enumeration types

An enumeration type is a set of named atomic enumeration values that are distinct from other values. You define the type using define-enum, and you reference enumeration values using colon notation:

(define-enum colors (red blue green))
(define favorite-color colors:green)

Displaying an enum just prints the enum name, but readable output using write (or the ~s format specifier) prepends the type name:

(format "~a" favorite-color) ⇒ "green"
(format "~s" favorite-color) ⇒ "colors:green"

The static values method returns a Java array of the enumeration values, in declaration order, while ordinal yields the index of an enumeration value:

(colors:values) ⇒ [red blue green]
((colors:values) 1) ⇒ blue
(favorite-color:ordinal) ⇒ 2

If you invoke the enumeration type as a function, it will map the name (as a string) to the corresponding value. (This uses the valueOf method.)

(colors "red") ⇒ red
(colors "RED") ⇒ throws IllegalArgumentException
(eq? favorite-color (colors:valueOf "green")) ⇒ #t

Kawa enumerations are based on Java enumerations. Thus the above is similar to a Java5 enum declaration, and the type colors above extends java.lang.Enum.

define-enum enum-type-name option-pair... (enum-value-name ...) field-or-method-decl...

This declares a new enumeration type enum-type-name, whose enumerations values are the enum-value-name list. You can specify extra options and members using option-pair and field-or-method-decl, which are as in define-simple-class. (The define-enum syntax is similar to a define-simple-class that extends java.lang.Enum.)

(Note that R6RS has a separate Enumerations library (rnrs enum). Unfortunately, this is not compatible with standard Java enums. R6RS enums are simple symbols, which means you cannot distinguish two enum values from different enumeration types if they have the same value, nor from a vanilla symbol. That makes them less useful.)

Annotations of declarations

The Java platform lets you associate with each declaration zero or more annotations. They provide an extensible mechanism to associate properties with declarations. Kawa support for annotations is not complete (the most important functionality missing is being able to declare annotation types), but is fairly functional. Here is a simple example illustrating use of JAXB annotations: an XmlRootElement annotation on a class, and an XmlElement annotation on a field:

(define-alias XmlRootElement javax.xml.bind.annotation.XmlRootElement)
(define-alias XmlElement javax.xml.bind.annotation.XmlElement)
(define-simple-class Bib ( ) (@XmlRootElement name: "bib")
  (books (@XmlElement name: "book" type: Book) ::java.util.ArrayList))
(define-simple-class Book () ...)

This tutorial explains the JAXB example in depth.

Here is the syntax:

annotation ::= (@annotation-typename annotations-element-values)
annotations-element-values ::= annotation-element-value
  | annotation-element-pair ...
annotation-element-pair ::= keyword annotation-element-value
annotation-element-value ::= expression
annotation-typename ::= expression

An annotations-element-values consisting of just a single annotation-element-value is equivalent to an annotation-element-pair with a value: keyword.

Each keyword must correspond to the name of an element (a zero-argument method) in the annotation type. The corresponding annotation-element-value must be compatible with the element type (return type of the method) of the annotation type.

Allowed element types are of the following kinds:

  • Primitive types, where the annotation-element-value must be number or boolean coercible to the element type.

  • Strings, where the annotation-element-value is normally a string literal.

  • Classes, where the annotation-element-value is normally a classname.

  • Enumeration types. The value usually has the form ClassName:enumFieldname.

  • Nested annotation types, where the annotation-element-value must be a compatible annotation value.

  • An array of one of the allowable types. An array constructor expression works, but using the square bracket syntax is recommended.

Annotations are usually used in declarations, where they are required to be “constant-folded” to compile-time constant annotation values. This is so they can be written to class files. However, in other contexts an annotation can be used as an expression with general sub-expressions evaluated at run-time:

(define bk-name "book")
(define be (@XmlElement name: bk-name type: Book))
(be:name) ⇒ "book"

(This may have limited usefulness: There are some bugs, including lack of support for default values for annotation elements. These bugs can be fixed if someone reports a need for runtime construction of annotation values.)

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.

You can declare a class using define-simple-class with the same name as the module class, for example the following in a file named foo.scm:

(define-simple-class foo ...)

In this case the defined class will serve dual-purpose as the module class.

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. (If compiling with the --main flag, then by default no identifiers are exported.) 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 (or export) declaration in the module, then only those names listed are exported. There can be more than one module-export, and they can be anywhere in the Scheme file. The recommended style has a single module-export near the beginning of the file.

module-export export-spec^*

export export-spec^*

The forms export and module-export are equivalent. (The older Kawa name is module-export; export comes from R7RS.) Either form specifies a list of identifiers which can be made visible to other libraries or programs.

export-spec ::= identifier
  | (rename identifier_1 identifier_2)

In the former variant, an identifier names a single binding defined within or imported into the library, where the external name for the export is the same as the name of the binding within the library. A rename spec exports the binding defined within or imported into the library and named by identifier_1, using identifier_2 as the external name.

Note that it is an error if there is no definition for identifier (or identifier_1) 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) ...)

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, 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.

module-name name

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.

module-extends class

Specifies that the class generated from the immediately surrounding module should extend (be a sub-class of) the class <class>.

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 the section called “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.

Unfortunately, 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 the section called “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.

Static vs non-static modules

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.

You can control whether a module is compiled to a static or a non-static class using either a command-line flag to the compiler, or using the module-static special form.

--module-static

If no module-static is specified, generate a static module (as if (module-static #t) were specified). This is (now) the default.

--module-nonstatic
--no-module-static

If no module-static is specified, generate a non-static module (as if (module-static #f) were specified). This used to be the default.

--module-static-run

If no module-static is specified, generate a static module (as if (module-static 'init-run) were specified).

module-static name ...

module-static #t

module-static #f

module-static 'init-run

Control whether the generated fields and methods are static. If #t or 'init-run is specified, then the module will be a static module, all definitions will be static. If 'init-run is specified, in addition the module body is evaluated in the class's static initializer. (Otherwise, it is run the first time it is require'd.) 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:

  1. If there is a module-extends or module-implements declaration, or one of the --applet or --servlet command-line flags was specified, then (module-static #f) is implied.

  2. If one of the command-line flags --no-module-static, --module-nonstatic, --module-static, or --module-static-run was specified, then the default is #f, #f, #t, or 'init-run, respectively.

  3. Otherwise the default is (module-static #t). (It used to be (module-static #f) in older Kawa versions.)

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.

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.)

Module options

Certain compilation options can be be specified either on the command-line when compiling, or in the module itself.

module-compile-options [key: value] ...

This sets the value of the key option to value for the current module (source file). It takes effect as soon it is seen during the first macro-expansion pass, and is active thereafter (unless overridden by with-compile-options).

The key: is one of the supported option names (The ending colon makes it a Kawa keyword). Valid option keys are:

  • main: - Generate an application, with a main method.

  • full-tailcalls: - Use a calling convention that supports proper tail recursion.

  • warn-undefined-variable: - Warn if no compiler-visible binding for a variable.

  • warn-unknown-member: - Warn if referencing an unknown method or field.

  • warn-invoke-unknown-method: - Warn if invoke calls an unknown method (subsumed by warn-unknown-member).

  • warn-unused: - Warn if a variable is usused or code never executed.

  • warn-unreachable: - Warn if this code can never be executed.

  • warn-void-used: - Warn if an expression depends on the value of a void sub-expression (one that never returns a value).

  • warn-as-error: - Treat a compilation warning as if it were an error.

The value must be a literal value: either a boolean (#t or #f), a number, or a string, depending on the key. (All the options so far are boolean options.)

(module-compile-options warn-undefined-variable: #t)
;; This causes a warning message that y is unknown.
(define (func x) (list x y))

with-compile-options [key: value] ... body

Similar to module-compile-options, but the option is only active within body.

The module option key main: has no effect when applied to a particular body via the with-compile-options syntax.

(define (func x)
  (with-compile-options warn-invoke-unknown-method: #f
    (invoke x 'size)))

Requiring (importing) a module

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

require 'featureName

require classname ["sourcepath"]

require "sourcepath"

Search for a matching module (class), and add the names exported by that module to the current set of visible names. Normally, the module is specified using classname. The module can be static module (all public fields must be static), or an instance module (it has a public default constructor).

If the module is a instance module 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 for both instance and static modules.) 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 a "sourcepath" is specified then that is used to locate the source file for the module, and if necessary, compile it.

If a 'featurename is specified then the featurename is looked up (at compile time) in the "feature table" which yields the implementing classname.

import import-set^*

Similar functionality as require, but specified by R6RS.

import-set ::= library-reference
  | (library library-reference )
  | (only import-set identifier^*)
  | (except import-set identifier^*)
  | (prefix import-set identifier )
  | (rename import-set ( identifier1 identifier2 )^*)
library-reference ::= ( identifier^+ )

A library-reference is mapped to a class name by concatenating all the identifiers, separated by dots. For example:

(import (gnu kawa slib srfi37))

is equivalent to:

(require gnu.kawa.slib.srfi37)

By default, all of an imported library's exported bindings are made visible within an importing library using the names given to the bindings by the imported library. The precise set of bindings to be imported and the names of those bindings can be adjusted with the only, except, prefix, and rename forms as described below.

  • An only form produces a subset of the bindings from another import-set, including only the listed identifiers. The included identifiers must be in the original import-set.

  • An except form produces a subset of the bindings from another import-set, including all but the listed identifiers. All of the excluded identifiers must be in the original import-set.

  • A prefix form adds the identifier prefix to each name from another import-set.

  • A rename form:

    (rename (identifier1 identifier2) …)
    

    removes the bindings for identifier1 to form an intermediate import-set, then adds the bindings back for the corresponding identifier2 to form the final import-set. Each identifier1 must be in the original import-set, each identifier2 must not be in the intermediate import-set, and the identifier2s must be distinct.

provide 'featurename

Declare that 'featurename is available. A following cond-expand in this scope will match featurename.

Using require and provide with featurenames is similar to the same-named macros in SLib, Emacs, and Common Lisp. However, in Kawa these are not functions, but instead they are syntax forms that are processed at compile time. That is why only quoted featurenames are supported. This is consistent with Kawa emphasis on compilation and static binding.

For some examples, you may want to look in the gnu/kawa/slib directory.

Record types

The define-record-type form can be used for creating new data types, called record types. A predicate, constructor, and field accessors and modifiers are defined for each record type. The define-record-type feature is specified by SRFI-9, which is implemented by many modern Scheme implementations.

define-record-type type-name (constructor-name field-tag ...) predicate-name (field-tag accessor-name [modifier-name]) ...

The form define-record-type is generative: each use creates a new record type that is distinct from all existing types, including other record types and Scheme's predefined types. Record-type definitions may only occur at top-level (there are two possible semantics for `internal' record-type definitions, generative and nongenerative, and no consensus as to which is better).

An instance of define-record-type is equivalent to the following definitions:

  • The type-name is bound to a representation of the record type itself.

  • The constructor-name is bound to a procedure that takes as many arguments as there are field-tags in the (constructor-name ...) subform and returns a new type-name record. Fields whose tags are listed with constructor-name have the corresponding argument as their initial value. The initial values of all other fields are unspecified.

  • The predicate-name is a predicate that returns #t when given a value returned by constructor-name and #f for everything else.

  • Each accessor-name is a procedure that takes a record of type type-name and returns the current value of the corresponding field. It is an error to pass an accessor a value which is not a record of the appropriate type.

  • Each modifier-name is a procedure that takes a record of type type-name and a value which becomes the new value of the corresponding field. The result (in Kawa) is the empty value #!void. It is an error to pass a modifier a first argument which is not a record of the appropriate type.

Set!ing the value of any of these identifiers has no effect on the behavior of any of their original values.

Here is an example of how you can define a record type named pare with two fields x and y:

(define-record-type pare
  (kons x y)
  pare?
  (x kar set-kar!)
  (y kdr))

The above defines kons to be a constructor, kar and kdr to be accessors, set-kar! to be a modifier, and pare? to be a predicate for pares.

(pare? (kons 1 2))        ⇒ #t
(pare? (cons 1 2))        ⇒ #f
(kar (kons 1 2))          ⇒ 1
(kdr (kons 1 2))          ⇒ 2
(let ((k (kons 1 2)))
  (set-kar! k 3)
  (kar k))                ⇒ 3

Kawa compiles the record type into a nested class. If the define-record-type appears at module level, the result is a class that is a member of the module class. For example if the above pare class is define in a module parelib, then the result is a class named pare with the internal JVM name parelib$pare. The define-record-type can appear inside a procedure, in which case the result is an inner class.

The nested class has a name derived from the type-name. If the type-name is valid Java class name, that becomes the name of the Java class. If the type-name has the form <name> (for example <pare>), then name is used, if possible, for the Java class name. Otherwise, the name of the Java class is derived by "mangling" the type-name. In any case, the package is the same as that of the surrounding module.

Kawa generates efficient code for the resulting functions, without needing to use run-time reflection.

Creating New Record Types On-the-fly

Calling the make-record-type procedure creates a new record data type at run-time, without any compile-time support. It is primarily provided for compatibility; in most cases it is better to use the define-record-type form (see the section called “Record types”).

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.

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.

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.

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.

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.

record? obj

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

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.

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.

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.

Records are extensions of the class Record. These procedures use the Java 1.1 reflection facility.

Calling Java methods from Scheme

You can call a Java method as if it were a Scheme procedure using various mechanisms.

Calling static methods using colon notation

The easiest way to invoke a static method is to use colon notation, specifically:

(class-expression:method-name argument ...)

The class-expression can be a class in the current lexical scope, such as a class defined using define-simple-class:

(define-simple-class MyClass ()
  ((add2 x y) allocation: 'static (+ x y)))
(MyClass:add2 3 4) ⇒ 7

Often class-expression is a fully-qualified class name:

(java.lang.Math:sqrt 9.0) ⇒ 3.0

This is only allowed when the name is of a class that exists and is accessible both at compile-time and run-time, and the name is not otherwise lexically bound.

You can also use a defined alias:

(define-alias jlMath java.lang.Math)
(jlMath:sqrt 16.0) ⇒ 4.0

You can even evaluate class-expression at run-time (in which case Kawa may have to use slower reflection):

(let ((math java.lang.Math)) math:sqrt 9.0) ⇒ 3.0

Here java.lang.Math evaluates to a java.lang.Class instance for the named class (like Java's java.lang.Class.class, again assuming the class exists and is accessible both at compile-time and run-time, and the name is not otherwise lexically bound.

Calling instance methods using colon notation

The syntax is:

(instance:method-name argument ...)

This invokes the method named method-name with the evaluated instance as the target object and the evaluated arguments as the method arguments.

For example:

((list 9 8 7):toString) ⇒ "(9 8 7)"
([5 6 7]:get 2) ⇒ 7

This older syntax is also available:

(*:method-name instance argument ...)

For example:

(*:toString (list 9 8 7))

You can also name the class explicitly:

(class-expression:method-name instance argument ...)

For example:

(java.util.List:get [5 6 7] 2) ⇒ 7

Using an explicit class is like coercing the instance:

(*:method-name (as class-expression instance )argument ...)

Note that for some special values, including java.lang.Class instances, you can't use the compact form of colon notation where the instance is before the comma:

(java.lang.Integer:getDeclaredField "MAX_VALUE") ⇒ error

This is because in this case we look for a static member of java.lang.Integer (at least as currently defined and implemented), while we want an instance member of java.lang.Class. In those cases you can use one of these alternative forms, which all return the same java.lang.reflect.Field result:

(*:getDeclaredField java.lang.Integer "MAX_VALUE")
(java.lang.Class:getDeclaredField java.lang.Integer "MAX_VALUE")
(invoke java.lang.Integer 'getDeclaredField "MAX_VALUE")

Method names

The method to invoke is selected using the specified method name and argments. If specified name is not a Java name, it is "mangled" (see the section called “Mapping Scheme names to Java names”) into a valid Java name. All accessible methods whose names match are considered. Methods that match after appending $V or $X or $V$X are also considered. A $V suffix matches a variable number of arguments: any excess arguments are collect into an gnu.lists.LList or a Java array (depending on the final parameter type). A $X specifies that the method expects an extra implicit CallContext parameter. In that case the method's result is written to the CallContext, so the method result type must be void.

(Kawa may compile a procedure with a #!rest or keyword args whose name is fn to a method named fn$V. It adds an implicit parameter for the extra arguments. By default this extra extra parameter is a Scheme list. You can specify a Java array type instead, in which case the method is named fn without the $V, and instead it is marked as a Java-5 varargs method. The array element type must be compatible with all the extra arguments.)

Invoking a method with the invoke function

If you prefer, you can instead use the following functions. (There is also an older deprecated lower-level interface (see ???.)

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.

Any accessible 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.

An example:

(invoke-static java.lang.Thread 'sleep 100)

The behavior of interpreted code and compiled code is not identical, 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 object name args ...

The name can be <symbol> or <string> that names one or more methods in the Java class.

Any accessible 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.

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)

or

(let ((my-date ::java.util.Date (calculate-date))
      (cur-date ::int (get-cur-date)))
  (invoke my-date 'setDate cur-date))

invoke-special class receiver-object name arg ...

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.

This procedure is very similar to invoke and invoke-static and invokes the specified method, ignoring any methods in subclasses that might overide it. One interesting use is to invoke a method in your super-class like the Java language super keyword.

Any methods in the specified class that match "name" or "name$V" collectively form a generic procedure. That generic procedure is then applied as in invoke using the receiver-object and the arguments (if any).

The compiler must be able to inline this procedure (because you cannot force a specific method to be called using reflection). Therefore the class and name must resolve at compile-time to a specific method.

(define-simple-class <MyClass> (<java.util.Date>)
  ((get-year) :: <int>
   (+ (invoke-special <java.util.Date> (this) 'get-year)) 1900)
  ((set-year (year :: <int>)) :: <void>
   (invoke-special <java.util.Date> (this) 'set-year (- year 1900))))

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.

Using a namespace prefix

This way of invoking a method is deprecated.

You can use define-namespace to define an alias for a Java class:

(define-namespace Int32 "class:java.lang.Integer")

In this example the name Int32 is a namespace alias for the namespace whose full name is "class:java.lang.Integer". The full name should be the 6 characters "class:" followed by the fully-qualified name of a Java class.

Instead of a vamespace-uri you can use a variable that names a class, usually of the form <classname>. The following is equivalent to the above:

(define-namespace Int32 <java.lang.Integer>)

However, there is one important difference: The <classname> is first searched in the lexical scope. It may resolve to a class defined in the current compilation unit (perhaps defined using define-simple-class), or imported from another module, or an alias (such as from define-alias). Only if <classname> is not found in the current scope is it tried as the class name classname.

You can name a method using a qualified name containing a colon. The part of the name before the colon is a namespace alias (in this case Int32), and the part of the name after the colon is the method name. For example:

(Int32:toHexString 255) ⇒ "ff"

This invokes the static method toHexString in the Java class java.lang.Integer, passing it the argument 255, and returning the String "ff".

The general syntax is

(prefix:method-name arg ...)

This invokes the method named method-name in the class corresponding to prefix, and the args are the method arguments.

You can use the method name new to construct new objects:

(Int32:new '|255|)

This is equivalent to the Java expression new Integer("255"). You can also write:

(Int32:new "255")

You can also call instance methods using a namespace prefix:

(Int32:doubleValue (Int32:new "00255"))

This returns the double value 255.0.

As a shorthand, you can use the name of a Java class instead of a namespace alias:

(java.lang.Integer:toHexString 255)
(java.lang.Object:toString some-value)

If Kawa sees a qualified name with a prefix that is not defined and that matches the name of a known class, then Kawa will automatically treat the prefix as a nickname for namespace uri like class:java.lang.Integer. Both conditions should be true at both compile-time and run-time. However, using an explicit define-namespace is recommended.

As a final shorthand you can use an identifier in handle brackets, such as an existing type alias like <list>. The following are all equivalent:

(<list>:list3 'a 'b 'c)

This is equivalent to:

(define-namespace prefix <list>
(prefix:list3 'a 'b 'c)

for some otherwise-unused prefix.

Allocating objects

The recommended way to create an instance of a type T is to “call” T as if it were a function, with the arguments used to initialize the object. If T is a class and T has a matching constructor, then the arguments will used for constructor arguments:

(java.util.StringTokenizer "this/is/a/test" "/")

(You can think of the type T as being coerced to an instance-constructor function.)

If T is a container or collection type, then typically the arguments will be used to specify the child or component values. Many standard Scheme procedures fit this convention. For example in Kawa list and vector evaluate to types, rather than procedures as in standard Scheme, but because types can be used as constructor functions it just works:

(list 'a (+ 3 4) 'c) ⇒ (a 7 c)
(vector 'a 'b 'c) ⇒ #(a b c)

Any class T that has a default constructor and an add method can be initialized this way. Examples are java.util collection classes, and jawa.awt and javax.swing containers.

(java.util.ArrayList 11 22 33) ⇒ [11, 22, 333]

The above expression is equivalent to:

(let ((tmp (java.util.ArrayList)))
  (tmp:add 11)
  (tmp:add 22)
  (tmp:add 33)
  tmp)

Allocating Java arrays (see the section called “Using Java Arrays”) uses a similar pattern:

(int[] 2 3 5 7 11)

Sometimes you want to set some named property to an initial value. You can do that using a keyword argument. For example:

(javax.swing.JButton text: "Do it!" tool-tip-text: "do it")

This is equivalent to using setter methods:

(let ((tmp (javax.swing.JButton)))
  (tmp:setText "Do it!")
  (tmp:setToolTipText "do it")
  tmp)

A keyword argument key-name: can can translated to either a setKeyName: or a addKeyName: method. The latter makes it convenient to add listeners:

(javax.swing.JButton
  text: "Do it!"
  action-listener:
   (object (java.awt.event.ActionListener)
     ((actionPerformed e) (do-the-action))))

This is equivalent to:

(let ((tmp (javax.swing.JButton)))
  (tmp:setText "Do it!")
  (tmp:addActionListener
    (object (java.awt.event.ActionListener)
      ((actionPerformed e) (do-the-action))))
  tmp)

Making use of so-called “SAM-conversion” (see the section called “Anonymous classes”) makes it even more convenient:

(javax.swing.JButton
  text: "Do it!"
  action-listener:
   (lambda (e) (do-the-action)))

The general case allows for a mix of constructor arguments, property keywords, and child values:

class-type constructor-value... property-initializer... child-value...
constructor-value ::= expression
property-initializer ::= keyword expression
child-value ::= expression

First an object is constructed with the constructor-value arguments (if any) passed to the object constructor; then named properties (if any) are used to initialize named properties; and then remaining arguments are used to add child values.

There is an ambiguity if there is no property-initializer - we can't distinguish between a constructor-value and a child-value. In that case, if there is a matching constructor method, then all of the arguments are constructor arguments; otherwise, there must a default constructor, and all of the arguments are child-value arguments.

There is a trick you can you if you need both constructor-value and child-value arguments: separate them with an “empty keyword” ||:. This matches a method named add, which means that the next argument effectively a child-value - as do all the remaining arguments. Example:

(let ((vec #(1 2 3)))
  (java.util.ArrayList vec ||: 4 5 6))
  ⇒ [1, 2, 3, 4, 5, 6]

The compiler rewrites these allocations expression to generated efficient bytecode, assuming that the “function” being applied is a type known by the compiler. Most of the above expressions also work if the type is applied at run-time, in which case Kawa has to use slower reflection:

(define iarr int[])
(apply iarr (list 3 4 5)) ⇒ [3 4 5]

However addXxx methods and SAM-conversion are currently only recognized in the case of a class known at compile-time, not at run-time.

Here is a working Swing demo illustrating many of these techniques:

(define-alias JButton javax.swing.JButton)
(define-simple-class HBox (javax.swing.Box)
  ((*init*) (invoke-special javax.swing.Box (this) '*init* 0)))
(define-alias JFrame javax.swing.JFrame)
(define-alias Box javax.swing.Box)

(define value 0)

(define txt
  (javax.swing.JLabel
   text: "0"))

(define (set-value i)
  (set! value i)
  (set! txt:text (number->string i)))

(define fr
  (JFrame
     title: "Hello!"
     (Box 1#|VERTICAL|# ||:
      (javax.swing.Box:createGlue)
      txt
      (javax.swing.Box:createGlue)
      (HBox
       (JButton ;; uses 1-argument constructor
	"Decrement" ;; constructor argument
	tool-tip-text: "decrement"
	action-listener: (lambda (e) (set-value (- value 1))))
       (javax.swing.Box:createGlue)
       (JButton ;; uses 0-argument constructor
	text: "Increment"
	tool-tip-text: "increment"
	action-listener: (lambda (e) (set-value (+ value 1))))))))
(fr:setSize 200 100)
(set! fr:visible #t)

If you prefer, you can use the older make special 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>. Equivalent to:

type args ...

Another (semi-deprecated) function is to use the colon notation with the new pseudo-function. The following three are all equivalent:

(java.awt.Point:new x: 4 y: 3)
(make java.awt.Point: x: 4 y: 3)
(java.awt.Point x: 4 y: 3)

Accessing object fields

Accessing static fields and properties

The recommmended way to access fields uses the colon notation. For static fields and properties the following is recommended:

class-expression:field-name

For example:

java.lang.Integer:MAX_VALUE

A property with a get method is equivalent to a field. The following are all equivalent:

java.util.Currency:available-currencies
java.util.Currency:availableCurrencies
(java.util.Currency:getAvailableCurrencies)

Just like for a method call, the class-expression can be a class in the current lexical scope, a fully-qualified class name, or more generally an expression that evaluates to a class.

Accessing instance fields and properties

The syntax is:

instance:field-name

The field-name can of course be the name of an actual object field, but it can also be the name of a property with a zero-argument get method. For example, if cal is a java.util-Calendar instance, then the following are all equivalent:

cal:time-zone
cal:timeZone
(cal:getTimeZone)
(cal:get-time-zone)

You can use colon notation to assign to a field:

(set! cal:time-zone TimeZone:default)

which is equivalent to:

(cal:setTimeZone (TimeZone:getDefault))

A Java array only has the length field, plus the class property:

(int[] 4 5 6):length ⇒ 3
(int[] 4 5 6):class:name ⇒ "int[]"

Using field and static-field methods

The following methods are useful in cases where colon notation is ambiguous, for example where there are both fields and methods with the same name. You might also prefer as a matter of style, to emphasise that a field is being accessed.

field object fieldname

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

The field name is "mangled" (see the section called “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" (or "isFieldname" for a boolean property).

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

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 accessible. This procedure has a setter, and so can be used as the first operand to set!.

If the fieldname is the special name class, then it returns the java.lang.Class object corresponding to class (which is usually a gnu.bytecode.ClassType object).

Examples:

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

slot-ref object fieldname

A synonym for (field object fieldname).

slot-set! object fieldname value

A synonym for (set! (field object fieldname) value).

Older colon-dot notation

There is older syntax where following the colon there is field name a following the colon and a period.

To access an static field named field-name use this syntax

(prefix:.field-name instance)

The prefix can be as discussed in See the section called “Calling Java methods from Scheme”. Here are 5 equivalent ways:

(java.lang.Integer:.MAX_VALUE)
(<java.lang.Integer>:.MAX_VALUE)
(define-namespace Int32 <java.lang.Integer>)
(Int32:.MAX_VALUE)
(define-namespace Integer "class:java.lang.Integer")
(Integer:.MAX_VALUE)
(define-alias j.l.Integer java.lang.Integer)
(j.l.Integer:.MAX_VALUE)

You can set a static field using this syntax:

(set! (prefix:.field-name) new-value)

The special field name class can be used to extract the java.lang.Class object for a class-type. For example:

(java.util.Vector:.class) ⇒ class java.util.Vector

To access a instance field named field-name use the following syntax. Note the period before the field-name.

(*:.field-name instance)

This syntax works with set! - to set the field use this syntax:

(set! (*:.field-name instance) new-value)

Here is an example:

(define p (list 3 4 5))
(*:.cdr p) ⇒ (4 5)
(set! (*:.cdr p) (list 6 7))
p ⇒ (3 6 7)

You can specify an explicit class:

(prefix:.field-name instance)

If prefix is bound to <class>, then the above is equivalent to:

(*:.field-name (as <class> instance))

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 in 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 initialis’, 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.

The above mangling is used to generate Java method names. Each top-level definition is also mapped to a Java field. The name of this field is also mangled, but using a mostly reversible mapping: The Scheme function ‘file-exists?’ is mapped to the method name ‘file$Mnexists$Qu’. Because ‘$’ is used to encode special characters, you should avoid using it in names in your source file.

Scheme types in Java

All Scheme values are implemented by sub-classes of ‘java.lang.Object’.

Scheme symbols are implemented using java.lang.String. (Don't be confused by the fact the Scheme sybols are represented using Java Strings, while Scheme strings are represented by gnu.lists.FString. It is just that the semantics of Java strings match Scheme symbols, but do not match mutable Scheme strings.) Interned symbols are presented as interned Strings. (Note that with JDK 1.1 string literals are automatically interned.)

Scheme integers are implemented by gnu.math.IntNum. Use the make static function to create a new IntNum from an int or a long. Use the intValue or longValue methods to get the int or long value of an IntNum.

A Scheme "flonum" is implemented by gnu.math.DFloNum.

A Scheme pair is implemented by gnu.lists.Pair.

A Scheme vector is implemented by gnu.lists.FVectror.

Scheme characters are implemented using gnu.text.Char.

Scheme strings are implemented using gnu.lists.FString.

Scheme procedures are all sub-classes of gnu.mapping.Procedure. The "action" of a ‘Procedure’ is invoked by using one of the ‘apply*’ methods: ‘apply0’, ‘apply1’, ‘apply2’, ‘apply3’, ‘apply4’, or ‘applyN’. Various sub-class of ‘Procedure’ provide defaults for the various ‘apply*’ methods. For example, a ‘Procedure2’ is used by 2-argument procedures. The ‘Procedure2’ class provides implementations of all the ‘apply*’ methods exceptapply2’, which must be provided by any class that extends Procedure2.

Using Java Arrays

Creating new Java arrays

To allocate a Java array you can use the array type specifier as a constructor function. For example, to allocate an array with room for 10 elements each of each is a primitive int:

(int[] length: 10)

You can specify the initial elements instead of the length:

(object[] 31 32 33 34)

This creates a 4-length array, initialized to the given values.

Note this is a variation of the generation object-allocation (see the section called “Allocating objects”) pattern. You can explicitly use the make function, if you prefer:

(make object[] 31 32 33 34)

If you specify a length, you can also specify initial values for selected elements. If you specify an index, in the form of a literal integer-valued keyword, then following elements are placed starting at that position.

(int[] length: 100 10 12 80: 15 16 50: 13 14)

This creates an array with 100 elements. Most of them are initialized to the default value of zero, but elements with indexes 0, 1, 50, 51, 80, 81 are initialized to the values 10, 12, 13, 14, 15, 16, respectively.

Accessing Java array elements

You can access the elements of a Java array by treating it as a one-argument function, where the argument is the index:

(define primes (integer[] 2 3 5 7 11 13))
(primes 0) ⇒ 2
(primes 5) ⇒ 13

You can set an element by treating the array as a function with a setter:

(set! (primes 0) -2)
(set! (primes 3) -7)
primes ⇒ [-2 3 5 -7 11 13]

To get the number of elements of an array, you can treat it as having a length field:

primes:length ⇒ 6

Here is a longer example. This is the actual definition of the standard gcd function. Note the args variable receives all the arguments on the form of an integer array. (This uses the Java5 varargs feature.)

(define (gcd #!rest (args ::integer[])) ::integer
  (let ((n ::int args:length))
    (if (= n 0)
	0
	(let ((result ::integer (args 0)))
	  (do ((i ::int 1 (+ i 1)))
	      ((>= i n) result)
	    (set! result (gnu.math.IntNum:gcd result (args i))))))))

The above example generates good code, thanks to judicious use of casts and type specifications. In general, if Kawa knows that a “function” is an array then it will generate efficient bytecode instructions for array operations.

Old low-level array macros

The deprecated ??? are also supported.

Loading Java functions into Scheme

When kawa -C compiles (see the section called “Compiling to a set of .class files”) a Scheme module it creates a class that implements the java.lang.Runnable interface. (Usually it is a class that extends the gnu.expr.ModuleBody.) It is actually fairly easy to write similar "modules" by hand in Java, which is useful when you want to extend Kawa with new "primitive functions" written in Java. For each function you need to create an object that extends gnu.mapping.Procedure, and then bind it in the global environment. We will look at these two operations.

There are multiple ways you can create a Procedure object. Below is a simple example, using the Procedure1 class, which is class extending Procedure that can be useful for one-argument procedure. You can use other classes to write procedures. For example a ProcedureN takes a variable number of arguments, and you must define applyN(Object[] args) method instead of apply1. (You may notice that some builtin classes extend CpsProcedure. Doing so allows has certain advantages, including support for full tail-recursion, but it has some costs, and is a bit trickier.)

import gnu.mapping.*;
import gnu.math.*;
public class MyFunc extends Procedure1
{
  // An "argument" that is part of each procedure instance.
  private Object arg0;

  public MyFunc(String name, Object arg0)
  {
    super(name);
    this.arg0 = arg0;
  }

  public Object apply1 (Object arg1)
  {
    // Here you can so whatever you want. In this example,
    // we return a pair of the argument and arg0.
    return gnu.lists.Pair.make(arg0, arg1);
  }
}

You can create a MyFunc instance and call it from Java:

  Procedure myfunc1 = new MyFunc("my-func-1", Boolean.FALSE);
  Object aresult = myfunc1.apply1(some_object);

The name my-func-1 is used when myfunc1 is printed or when myfunc1.toString() is called. However, the Scheme variable my-func-1 is still not bound. To define the function to Scheme, we can create a "module", which is a class intended to be loaded into the top-level environment. The provides the definitions to be loaded, as well as any actions to be performed on loading

public class MyModule
{
  // Define a function instance.
  public static final MyFunc myfunc1
    = new MyFunc("my-func-1", IntNum.make(1));
}

If you use Scheme you can use require:

#|kawa:1|# (require <MyModule>)
#|kawa:2|# (my-func-1 0)
(1 0)

Note that require magically defines my-func-1 without you telling it to. For each public final field, the name and value of the field are entered in the top-level environment when the class is loaded. (If there are non-static fields, or the class implements Runnable, then an instance of the object is created, if one isn't available.) If the field value is a Procedure (or implements Named), then the name bound to the procedure is used instead of the field name. That is why the variable that gets bound in the Scheme environment is my-func-1, not myfunc1.

Instead of (require <MyModule>), you can do (load "MyModule") or (load "MyModule.class"). If you're not using Scheme, you can use Kawa's -f option:

$ kawa -f MyModule --xquery --
#|kawa:1|# my-func-1(3+4)
<list>1 7</list>

If you need to do some more complex calculations when a module is loaded, you can put them in a run method, and have the module implement Runnable:

public class MyModule implements Runnable
{
  public void run ()
  {
    Interpreter interp = Interpreter.getInterpreter();
    Object arg = Boolean.TRUE;
    interp.defineFunction (new MyFunc ("my-func-t", arg));
    System.err.println("MyModule loaded");
  }
}

Loading MyModule causes "MyModule loaded" to be printed, and my-func-t to be defined. Using Interpreter's defineFunction method is recommended because it does the righ things even for languages like Common Lisp that use separate "namespaces" for variables and functions.

A final trick is that you can have a Procedure be its own module:

import gnu.mapping.*;
import gnu.math.*;
public class MyFunc2 extends Procedure2
{
  public MyFunc(String name)
  {
    super(name);
  }

  public Object apply2 (Object arg1, arg2)
  {
    return gnu.lists.Pair.make(arg1, arg2);
  }

  public static final MyFunc myfunc1 = new MyFunc("my-func-2);
}

Evaluating Scheme expressions from Java

The following methods are recommended if you need to evaluate a Scheme expression from a Java method. (Some details (such as the ‘throws’ lists) may change.)

voidScheme.registerEnvironment ()

Initializes the Scheme environment. Maybe needed if you try to load a module compiled from a Scheme source file.

ObjectScheme.eval (InPort port, Environment env)

Read expressions from port, and evaluate them in the env environment, until end-of-file is reached. Return the value of the last expression, or Interpreter.voidObject if there is no expression.

ObjectScheme.eval (String string, Environment env)

Read expressions from string, and evaluate them in the env environment, until the end of the string is reached. Return the value of the last expression, or Interpreter.voidObject if there is no expression.

ObjectScheme.eval (Object sexpr, Environment env)

The sexpr is an S-expression (as may be returned by read). Evaluate it in the env environment, and return the result.

For the Environment in most cases you could use ‘Environment.current()’. Before you start, you need to initialize the global environment, which you can with

Environment.setCurrent(new Scheme().getEnvironment());

Alternatively, rather than setting the global environment, you can use this style:

Scheme scm = new Scheme();
Object x = scm.eval("(+ 3 2)");
System.out.println(x);

Using javax.script portable Java scripting

Kawa also supports the standard javax.script API. The main advantage of this API is if you want your users to be able to chose between multiple scripting languages. That way you can support Kawa without Kawa-specific programming.

For example the standard JDK tool jrunscript provides a read-eval-print-loop for any language that implements the javax.script API. It knows nothing about Kawa but can still use it:

$ jrunscript -cp kawa.jar -l scheme
scheme> (cadr '(3 4 5))
4

(Of course the jrunscript REPL isn't as nice as the one that Kawa provides. For example the latter can handle multi-line inputs.)